diff --git a/main.cpp b/main.cpp index 0ed30c6..2bc4aec 100644 --- a/main.cpp +++ b/main.cpp @@ -17,6 +17,9 @@ struct Mouse { int x, y; bool buttons[3]; + bool mouse_down[3]; + bool mouse_up[3]; + bool mouse_old[3]; }; typedef bool(*LoopFun)(void); @@ -56,14 +59,24 @@ unsigned char* map = nullptr; int map_x = 0; int map_y = 0; +#define UNDO_MAX 16 +unsigned char* undo = nullptr; +int undo_pointer = 0; +int undo_min = 0; +int undo_max = 0; + int tile_sel = 0; int tile_back = 0; const char* GetPath(const char* filename) { +#ifdef __APPLE__ static char fullpath[400]; strcpy(fullpath, path); strcat(fullpath, filename); return fullpath; +#else + return filename; +#endif } int StrToInt(const char* str) { @@ -76,6 +89,35 @@ int StrToInt(const char* str) { return val; } +#define UndoInc(var) (var = (var+1)%UNDO_MAX) +#define UndoDec(var) (var = (var-1+UNDO_MAX)%UNDO_MAX) +void Undo_Create() { + unsigned char* dest = undo + (map_width*map_height*undo_pointer); + memcpy(dest, map, map_width*map_height); +} +void Undo_Restore() { + unsigned char* src = undo + (map_width*map_height*undo_pointer); + memcpy(map, src, map_width*map_height); +} +void Undo_Op() { + UndoInc(undo_pointer); + Undo_Create(); + undo_max = undo_pointer; + if (undo_pointer == undo_min) UndoInc(undo_min); +} +void Undo_Undo() { + if (undo_pointer != undo_min) { + Undo_Restore(); + UndoDec(undo_pointer); + } +} +void Undo_Redo() { + if (undo_pointer != undo_max) { + UndoInc(undo_pointer); + Undo_Restore(); + } +} + void LoadConfig() { FILE* f = fopen(GetPath("config.ini"), "r"); if (!f) { error = 1; return; } @@ -113,7 +155,7 @@ void Init() { LoadConfig(); SDL_Init(SDL_INIT_EVERYTHING); - sdlWindow = SDL_CreateWindow("Mappy v0.1", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, screen_width*zoom, screen_height*zoom, SDL_WINDOW_SHOWN); + sdlWindow = SDL_CreateWindow("Mappy v0.3", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, screen_width*zoom, screen_height*zoom, SDL_WINDOW_SHOWN); sdlRenderer = SDL_CreateRenderer(sdlWindow, -1, SDL_RENDERER_PRESENTVSYNC); SDL_SetRenderDrawBlendMode(sdlRenderer, SDL_BLENDMODE_BLEND); SDL_RenderSetLogicalSize(sdlRenderer, screen_width, screen_height); @@ -164,6 +206,7 @@ void Init() { f = fopen(GetPath(map_filename), "rb"); map = (unsigned char*)malloc(map_width*map_height); + undo = (unsigned char*)malloc(map_width*map_height*UNDO_MAX); if (f) { int old_map_width = 0, old_map_height = 0; fread(&old_map_width, 1, 1, f); @@ -270,6 +313,14 @@ bool Update() { mouse.x /= zoom; mouse.y /= zoom; + mouse.mouse_down[0] = mouse.mouse_down[1] = mouse.mouse_down[2] = mouse.mouse_up[0] = mouse.mouse_up[1] = mouse.mouse_up[2] = false; + if ((buttons & 1) == 1) { if (!mouse.mouse_old[0]) mouse.mouse_down[0] = true; } else { if (mouse.mouse_old[0]) mouse.mouse_up[0] = true; } + if ((buttons & 2) == 2) { if (!mouse.mouse_old[1]) mouse.mouse_down[1] = true; } else { if (mouse.mouse_old[1]) mouse.mouse_up[1] = true; } + if ((buttons & 4) == 4) { if (!mouse.mouse_old[2]) mouse.mouse_down[2] = true; } else { if (mouse.mouse_old[2]) mouse.mouse_up[2] = true; } + mouse.mouse_old[0] = (buttons & 1) == 1; + mouse.mouse_old[1] = (buttons & 2) == 2; + mouse.mouse_old[2] = (buttons & 4) == 4; + if (buttons == 0) ignoreMouse = false; if (buttons != 0 && !ignoreMouse) { @@ -343,6 +394,44 @@ void DoTilePal() { if (keyJustPressed == SDL_SCANCODE_Q) state = STATE_TILEMAP; } +void floodfill_r(const int x, const int y, const int tile) { + const int width_in_tiles = screen_width / tile_width; + const int height_in_tiles = screen_height / tile_height; + if ( (keyboard[SDL_SCANCODE_LCTRL] ) || ( (x >= map_x && y >= map_y && x < (map_x + width_in_tiles) && y < (map_y + height_in_tiles)) ) ) { + if (map[x + y * map_width] == tile) { + map[x + y * map_width] = tile_sel; + floodfill_r(x + 1, y, tile); + floodfill_r(x - 1, y, tile); + floodfill_r(x, y + 1, tile); + floodfill_r(x, y - 1, tile); + } + } +} + +void FloodFill() { + const int x = map_x + (mouse.x / tile_width); + const int y = map_y + (mouse.y / tile_height); + const int tile = map[x + y * map_width]; + if (tile != tile_sel) floodfill_r(x, y, tile); +} + +void ReplaceTile() { + const int x = map_x + (mouse.x / tile_width); + const int y = map_y + (mouse.y / tile_height); + const int tile = map[x + y * map_width]; + const bool all_map = keyboard[SDL_SCANCODE_LCTRL]; + const int xi = all_map ? 0 : map_x; + const int yi = all_map ? 0 : map_y; + const int w = all_map ? map_width : xi+(screen_width / tile_width); + const int h = all_map ? map_height : yi+(screen_height / tile_height); + + for (int yy = yi; yy < h; yy++) { + for (int xx = xi; xx < w; xx++) { + if (map[xx + yy * map_width] == tile) map[xx + yy * map_width] = tile_sel; + } + } +} + void DoTileMap() { static bool menu = false; @@ -362,7 +451,8 @@ void DoTileMap() { DrawRect(cur_x*tile_width - 1, cur_y*tile_height - 1, tile_width + 2, tile_height + 2, 255, 255, 255, 128); if (keyJustPressed == SDL_SCANCODE_TAB) { - menu = true; return; } + menu = true; return; + } if (keyJustPressed == SDL_SCANCODE_Q) state = STATE_TILEPAL; if (keyJustPressed == SDL_SCANCODE_E) { state = STATE_MINIMAP; UpdateMiniMap(); } if (keyJustPressed == SDL_SCANCODE_X) { int temp = tile_back; tile_back = tile_sel; tile_sel = temp; } @@ -370,10 +460,19 @@ void DoTileMap() { if (keyJustPressed == SDL_SCANCODE_LEFT || keyJustPressed == SDL_SCANCODE_A) if (map_x > 0) map_x -= width_in_tiles; if (keyJustPressed == SDL_SCANCODE_DOWN || keyJustPressed == SDL_SCANCODE_S) if (map_y < map_height - height_in_tiles) map_y += height_in_tiles; if (keyJustPressed == SDL_SCANCODE_UP || keyJustPressed == SDL_SCANCODE_W) if (map_y > 0) map_y -= height_in_tiles; + if (keyJustPressed == SDL_SCANCODE_F) { Undo_Op(); FloodFill(); } + if (keyJustPressed == SDL_SCANCODE_R) { Undo_Op(); ReplaceTile(); } + + if (mouse.mouse_down[MOUSE_BUTTON_LEFT] || mouse.mouse_down[MOUSE_BUTTON_RIGHT]) { Undo_Op(); } if (mouse.buttons[MOUSE_BUTTON_RIGHT]) { map[(map_x + cur_x) + (map_y + cur_y)*map_width] = tile_back; } if (mouse.buttons[MOUSE_BUTTON_LEFT]) { map[(map_x + cur_x) + (map_y + cur_y)*map_width] = tile_sel; } if (mouse.buttons[MOUSE_BUTTON_MIDDLE]) { tile_sel = map[(map_x + cur_x) + (map_y + cur_y)*map_width]; } - } else { + + if (keyJustPressed == SDL_SCANCODE_Z && keyboard[SDL_SCANCODE_LCTRL]) { Undo_Undo(); } + if (keyJustPressed == SDL_SCANCODE_Y && keyboard[SDL_SCANCODE_LCTRL]) { Undo_Redo(); } + if (keyJustPressed == SDL_SCANCODE_Z && keyboard[SDL_SCANCODE_LCTRL] && keyboard[SDL_SCANCODE_LSHIFT]) { Undo_Redo(); } + } + else { if (keyJustPressed == SDL_SCANCODE_TAB) { menu = false; return; } FillRect(0, 0, screen_width, screen_height, 0, 0, 0, 192); if (Button(6, 6, "Save")) { @@ -385,6 +484,7 @@ void DoTileMap() { menu = false; ignoreMouse = true; } if (Button(6, 24, "Clear")) { + Undo_Op(); for (int y = 0; y < height_in_tiles; y++) { for (int x = 0; x < width_in_tiles; x++) { map[(map_x + x) + (map_y + y) * map_width] = tile_back; @@ -393,6 +493,7 @@ void DoTileMap() { menu = false; ignoreMouse = true; } if (Button(6, 42, "Border")) { + Undo_Op(); for (int y = 0; y < height_in_tiles; y++) { map[(map_x) + (map_y + y) * map_width] = tile_sel; map[(map_x + width_in_tiles - 1) + (map_y + y) * map_width] = tile_sel; @@ -409,6 +510,7 @@ void DoTileMap() { #define mod(x, y) (((x % y)+y)%y) void ShiftMap(int sx, int sy) { + Undo_Op(); int test = mod(-1, 240); sx *= (screen_width/tile_width); sy *= (screen_height / tile_height); @@ -447,6 +549,7 @@ void DoMiniMap() { const int screen_height_in_tiles = screen_height / tile_height; DrawRect((ox + map_x) - 1, (oy + map_y) - 1, screen_width_in_tiles + 2, screen_height_in_tiles + 2, 255, 255, 255); //DrawRect(ox + (tile_sel % tiles_per_row)*tile_width - 1, oy + (tile_sel / tiles_per_row)*tile_height - 1, tile_width + 2, tile_height + 2, 255, 255, 255); + DrawRect(ox - 1, oy - 1, map_width + 2, map_height + 2, 255, 0, 0); if (mouse.buttons[MOUSE_BUTTON_LEFT]) { map_x = int((mouse.x-ox) / screen_width_in_tiles) * screen_width_in_tiles; map_y = int((mouse.y - oy) / screen_height_in_tiles) * screen_height_in_tiles;