|
|
|
#include <SDL2/SDL.h>
|
|
|
|
#include <SDL2/SDL_events.h>
|
|
|
|
#include <SDL2/SDL_pixels.h>
|
|
|
|
#include <SDL2/SDL_render.h>
|
|
|
|
#include <SDL2/SDL_scancode.h>
|
|
|
|
#include <SDL2/SDL_surface.h>
|
|
|
|
#include <SDL2/SDL_timer.h>
|
|
|
|
#include <SDL2/SDL_video.h>
|
|
|
|
#include <deque>
|
|
|
|
#include <iostream>
|
|
|
|
#include <string>
|
|
|
|
#include <vector>
|
|
|
|
#ifdef __EMSCRIPTEN__
|
|
|
|
#include <emscripten.h>
|
|
|
|
#include <emscripten/html5.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "particles.h"
|
|
|
|
|
|
|
|
namespace pengine {
|
|
|
|
|
|
|
|
class GameWindow {
|
|
|
|
private:
|
|
|
|
std::string name;
|
|
|
|
|
|
|
|
public:
|
|
|
|
struct SDL_Window *window;
|
|
|
|
struct SDL_Renderer *renderer;
|
|
|
|
|
|
|
|
GameWindow(int width, int height, std::string name) {
|
|
|
|
SDL_Init(SDL_INIT_VIDEO);
|
|
|
|
this->name = name;
|
|
|
|
// SDL_SetHint(SDL_HINT_EMSCRIPTEN_KEYBOARD_ELEMENT, "#canvas");
|
|
|
|
window = SDL_CreateWindow(name.c_str(), SDL_WINDOWPOS_CENTERED,
|
|
|
|
SDL_WINDOWPOS_CENTERED, width, height,
|
|
|
|
SDL_WINDOW_BORDERLESS);
|
|
|
|
renderer = SDL_CreateRenderer(
|
|
|
|
window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
|
|
|
|
}
|
|
|
|
|
|
|
|
~GameWindow() {
|
|
|
|
SDL_DestroyRenderer(renderer);
|
|
|
|
SDL_DestroyWindow(window);
|
|
|
|
SDL_Quit();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
enum MouseAction { NONE, LEFT, RIGHT };
|
|
|
|
|
|
|
|
class PaintTool {
|
|
|
|
public:
|
|
|
|
int particle_selected = 0;
|
|
|
|
int size = 1;
|
|
|
|
MouseAction mouse_state = NONE;
|
|
|
|
bool paused = false;
|
|
|
|
};
|
|
|
|
|
|
|
|
class Game {
|
|
|
|
GameWindow win;
|
|
|
|
Grid grid;
|
|
|
|
SDL_Texture *grid_texture;
|
|
|
|
SDL_Renderer *ren;
|
|
|
|
int internal_sz[2];
|
|
|
|
int window_sz[2];
|
|
|
|
struct su_pair {
|
|
|
|
int x;
|
|
|
|
int y;
|
|
|
|
} coord;
|
|
|
|
|
|
|
|
std::vector<Particle *> particles = std::vector<Particle *>();
|
|
|
|
PaintTool tool = PaintTool();
|
|
|
|
|
|
|
|
public:
|
|
|
|
Game(int x, int y, int gx, int gy)
|
|
|
|
: win{GameWindow{x, y, "sand"}}, grid{Grid{gx, gy}} {
|
|
|
|
ren = win.renderer;
|
|
|
|
|
|
|
|
/* texture for drawing particles into */
|
|
|
|
grid_texture = SDL_CreateTexture(ren, SDL_PIXELFORMAT_ARGB8888,
|
|
|
|
SDL_TEXTUREACCESS_STATIC, gx, gy);
|
|
|
|
|
|
|
|
internal_sz[0] = gx;
|
|
|
|
internal_sz[1] = gy;
|
|
|
|
window_sz[0] = x;
|
|
|
|
window_sz[1] = y;
|
|
|
|
|
|
|
|
particles.push_back(new Sand());
|
|
|
|
particles.push_back(new Water());
|
|
|
|
particles.push_back(new Steam());
|
|
|
|
particles.push_back(new Lava());
|
|
|
|
}
|
|
|
|
|
|
|
|
~Game() {
|
|
|
|
SDL_DestroyTexture(grid_texture);
|
|
|
|
for (auto p : particles) {
|
|
|
|
delete p;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void update() { grid.update(); }
|
|
|
|
|
|
|
|
void draw_tool() {
|
|
|
|
if (tool.size == 1) {
|
|
|
|
int i = coord.x;
|
|
|
|
int j = coord.y;
|
|
|
|
if (grid.inside_grid(i, j)) {
|
|
|
|
auto colour = PixelColour(grid.pixels[i + j * grid.xsize]);
|
|
|
|
colour.r = 255 - colour.r;
|
|
|
|
colour.g = 255 - colour.g;
|
|
|
|
colour.b = 255 - colour.b;
|
|
|
|
grid.pixels[i + j * grid.xsize] = colour.get_pixelcol();
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = coord.x - tool.size / 2; i < coord.x + tool.size / 2; i++) {
|
|
|
|
for (int j = coord.y - tool.size / 2; j < coord.y + tool.size / 2; j++) {
|
|
|
|
if (grid.inside_grid(i, j)) {
|
|
|
|
auto colour = PixelColour(grid.pixels[i + j * grid.xsize]);
|
|
|
|
colour.r = 255 - colour.r;
|
|
|
|
colour.g = 255 - colour.g;
|
|
|
|
colour.b = 255 - colour.b;
|
|
|
|
grid.pixels[i + j * grid.xsize] = colour.get_pixelcol();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void remove_particles() {
|
|
|
|
if (tool.size <= 1) {
|
|
|
|
if (grid.inside_grid(coord.x, coord.y)) {
|
|
|
|
grid.erase_particle_at(coord.x, coord.y);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = coord.x - tool.size / 2; i < coord.x + tool.size / 2; i++) {
|
|
|
|
for (int j = coord.y - tool.size / 2; j < coord.y + tool.size / 2; j++) {
|
|
|
|
if (grid.inside_grid(i, j)) {
|
|
|
|
grid.erase_particle_at(i, j);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void add_particles() {
|
|
|
|
if (tool.size <= 1) {
|
|
|
|
if (grid.inside_grid(coord.x, coord.y)) {
|
|
|
|
grid.add_particle_at(coord.x, coord.y,
|
|
|
|
particles[tool.particle_selected]->id);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = coord.x - tool.size / 2; i < coord.x + tool.size / 2; i++) {
|
|
|
|
for (int j = coord.y - tool.size / 2; j < coord.y + tool.size / 2; j++) {
|
|
|
|
if (grid.inside_grid(i, j)) {
|
|
|
|
grid.add_particle_at(i, j, particles[tool.particle_selected]->id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void draw() {
|
|
|
|
SDL_SetRenderDrawColor(ren, 40, 40, 40, 255);
|
|
|
|
|
|
|
|
SDL_RenderClear(ren);
|
|
|
|
|
|
|
|
grid.update_texture();
|
|
|
|
draw_tool();
|
|
|
|
|
|
|
|
SDL_UpdateTexture(grid_texture, NULL, grid.pixels,
|
|
|
|
grid.xsize * sizeof(Uint32));
|
|
|
|
SDL_RenderCopy(ren, grid_texture, NULL, NULL);
|
|
|
|
SDL_RenderPresent(ren);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct su_pair get_click_location_in_grid(int x, int y) {
|
|
|
|
struct su_pair p;
|
|
|
|
p.x = (internal_sz[0] * x / window_sz[0]) % internal_sz[0];
|
|
|
|
p.y = (internal_sz[1] * y / window_sz[1]) % internal_sz[1];
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool run() {
|
|
|
|
SDL_Event event;
|
|
|
|
while (SDL_PollEvent(&event)) {
|
|
|
|
switch (event.type) {
|
|
|
|
case SDL_QUIT:
|
|
|
|
return true;
|
|
|
|
case SDL_WINDOWEVENT_RESIZED:
|
|
|
|
SDL_GetWindowSize(win.window, internal_sz, internal_sz + 1);
|
|
|
|
break;
|
|
|
|
case SDL_KEYDOWN:
|
|
|
|
|
|
|
|
if (event.key.keysym.scancode == SDL_SCANCODE_ESCAPE) {
|
|
|
|
tool.paused = !tool.paused;
|
|
|
|
}
|
|
|
|
if (event.key.keysym.scancode == SDL_SCANCODE_P) {
|
|
|
|
tool.paused = !tool.paused;
|
|
|
|
}
|
|
|
|
if (event.key.keysym.scancode == SDL_SCANCODE_C) {
|
|
|
|
grid.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (event.key.keysym.scancode == SDL_SCANCODE_1) {
|
|
|
|
tool.particle_selected = 0;
|
|
|
|
};
|
|
|
|
if (event.key.keysym.scancode == SDL_SCANCODE_2) {
|
|
|
|
tool.particle_selected = 1;
|
|
|
|
};
|
|
|
|
if (event.key.keysym.scancode == SDL_SCANCODE_3) {
|
|
|
|
tool.particle_selected = 2;
|
|
|
|
};
|
|
|
|
if (event.key.keysym.scancode == SDL_SCANCODE_4) {
|
|
|
|
tool.particle_selected = 3;
|
|
|
|
};
|
|
|
|
|
|
|
|
break;
|
|
|
|
case SDL_MOUSEMOTION:
|
|
|
|
coord = get_click_location_in_grid(event.motion.x, event.motion.y);
|
|
|
|
break;
|
|
|
|
case SDL_KEYUP:
|
|
|
|
break;
|
|
|
|
case SDL_MOUSEBUTTONDOWN:
|
|
|
|
if (event.button.button == 1) {
|
|
|
|
tool.mouse_state = LEFT;
|
|
|
|
} else if (event.button.button == 3) {
|
|
|
|
tool.mouse_state = RIGHT;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SDL_MOUSEBUTTONUP:
|
|
|
|
tool.mouse_state = NONE;
|
|
|
|
break;
|
|
|
|
case SDL_MOUSEWHEEL:
|
|
|
|
tool.size += event.wheel.y;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tool.mouse_state == LEFT) {
|
|
|
|
add_particles();
|
|
|
|
} else if (tool.mouse_state == RIGHT) {
|
|
|
|
remove_particles();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!tool.paused) {
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
|
|
|
|
draw();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
} // namespace pengine
|
|
|
|
|
|
|
|
pengine::Game *g_game;
|
|
|
|
void main_loop() { g_game->run(); }
|
|
|
|
|
|
|
|
int main(int argc, char **argv) {
|
|
|
|
|
|
|
|
const char *error = SDL_GetError();
|
|
|
|
if (error) {
|
|
|
|
std::cout << error;
|
|
|
|
}
|
|
|
|
bool quit = false;
|
|
|
|
|
|
|
|
int x = 500;
|
|
|
|
int y = 500;
|
|
|
|
#ifdef __EMSCRIPTEN__
|
|
|
|
EMSCRIPTEN_RESULT res = emscripten_get_canvas_element_size("#canvas", &x, &y);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
pengine::Game game{pengine::Game(x, y, x, y)};
|
|
|
|
g_game = &game;
|
|
|
|
|
|
|
|
#ifdef __EMSCRIPTEN__
|
|
|
|
emscripten_set_main_loop(main_loop, 0, true);
|
|
|
|
#else
|
|
|
|
while (!quit) {
|
|
|
|
quit = game.run();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|