You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
286 lines
6.9 KiB
286 lines
6.9 KiB
#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; |
|
}
|
|
|