#include #include #include #include #include #include #include #include #include #include #include #include #ifdef __EMSCRIPTEN__ #include #include #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 particles = std::vector(); 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; }