1 /** \file editMode.cc
2    Enables the editing of a given map.
3 */
4 /*
5    Copyright (C) 2000  Mathias Broxvall
6                        Yannick Perret
7 
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12 
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17 
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21 */
22 
23 #include "editMode.h"
24 
25 #include "editMode_codes.h"
26 #include "editWindows.h"
27 #include "game.h"
28 #include "mainMode.h"
29 #include "map.h"
30 #include "menusystem.h"
31 #include "settings.h"
32 #include "setupMode.h"
33 #include "smartTrigger.h"
34 #include "trigger.h"
35 
36 #include <SDL2/SDL_keyboard.h>
37 #include <SDL2/SDL_mouse.h>
38 #include <string.h>
39 #include <sys/stat.h>
40 
41 /* See cMenuNames_i18n inside EditMode::init for the values here */
42 char* cMenuNames[N_SUBMENUS];
43 
44 /* Names of all submeny entries.
45    If name begins with '*' then it is just decoration.
46    If name begins with '/' then don't prepend shortcut key (already part of name).
47    Otherwise, prepend shortcut key and make a selectagle area of the menu entry.
48    The menu codes in editMode_codes must correspond to the menus placement in this struct.
49 */
50 /* See cMenuEntries_i18n inside EditMode::init for the values here */
51 char* cMenuEntries[N_SUBMENUS][MAX_MENU_ENTRIES];
52 
53 /* Explanation: one string per menu. Each character corresponds to one shortcut key, * means no
54    shortcut
55    available. If multiple entries share the same shortcut it is the currently active entry that
56    wins or
57    the lowest numbered if none of them are currently open.
58 */
59 const char* cKeyShortcuts[N_SUBMENUS] = {
60     /* File */
61     "***sq*",
62     /* Edit */
63     "***** umkhj+-",
64     /* Colour */
65     "1234",
66     /* Flag */
67     "12345678*t",
68     /* Features */
69     "1234567",
70     /* Repair */
71     "1234567890",
72     /* Move */
73     "UDLR*******rcxv",
74     /* Window */
75     "***",
76     /* View */
77     "brlc",
78 };
79 
80 #define FLAG_ICE 0
81 #define FLAG_ACID 1
82 #define FLAG_SAND 2
83 #define FLAG_KILL 3
84 #define FLAG_TRAMPOLINE 4
85 #define FLAG_NOGRID 5
86 #define FLAG_TRACK 6
87 #define FLAG_FLAT 7
88 #define NUM_FLAGS 8
89 
90 char* flagNames[NUM_FLAGS];
91 
92 #define HILL_SPIKE 0
93 #define HILL_SMALL 1
94 #define HILL_MEDIUM 2
95 #define HILL_LARGE 3
96 #define HILL_HUGE 4
97 #define SMOOTH_SMALL 5
98 #define SMOOTH_LARGE 6
99 #define N_HILLS 7
100 
101 char* hillNames[N_HILLS];
102 
103 #define MAX_SUBCOMMANDS 10
104 enum { tlFile = 0, tlEdit = 1, tlView = 2, tlFlags = 3, tlTextures = 4, tlSize = 5 };
105 enum { subFileSave = 0, subFileQuit };
106 enum { subEditRegion = 10 };
107 enum { subViewBirdsEye = 20, subViewSwitch, subViewCrosshair, subViewClearWalls };
108 enum {
109   subFlagsIce = 30,
110   subFlagsAcid = 31,
111   subFlagsSand = 32,
112   subFlagsKill = 33,
113   subFlagsTrampoline = 34,
114   subFlagsNoGrid = 35,
115   subFlagsTrack = 36,
116   subFlagsFlat = 37,
117 };
118 enum { subTexturesNext = 40, subTexturesPrev, subTexturesRotate };
119 
120 enum {
121   subRepairCellCont = 50,
122   subRepairWaterCont,
123   subRepairColorCont,
124   subRepairCellCenters,
125   subRepairWaterCenters,
126   subRepairColorCenters,
127   subRepairCellRound,
128   subRepairWaterRound,
129   subRepairColorRound,
130   subRepairVelRound,
131 };
132 
133 enum {
134   editModeHeight = 0,
135   editModeColor,
136   editModeWater,
137   editModeVelocity,
138   editModeNoLines,
139   editModeFeatures,
140   nEditModes
141 };
142 
143 /* These are all the codes for different areas of the screen that can be selected */
144 #define CODE_FROM_COMMAND(x) (10 + (x))
145 #define CODE_TO_COMMAND(x) ((x)-10)
146 /*
147 #define CODE_EDITMODE    100
148 */
149 #define CODE_CELL_N 201
150 #define CODE_CELL_E 202
151 #define CODE_CELL_S 203
152 #define CODE_CELL_W 204
153 #define CODE_CELL_C 205
154 #define CODE_CELL_ALL 206
155 
156 int switchViewpoint = 0, birdsEye = 0;
157 int markX = -1, markY;
158 int doAskSave = 0;
159 
160 int selectedMenu = -1;
161 
162 const int viewPoints[4][2] = {{1, 1}, {1, -1}, {-1, -1}, {-1, 1}};
163 
164 EditMode* EditMode::editMode = NULL;
165 
loadStrings()166 void EditMode::loadStrings() {
167   char* cMenuNames_i18n[N_SUBMENUS] = {
168       /* TRANSLATORS: This is a list of all the menus in the map editor. */
169       _("File"),   _("Edit"), _("Color"),  _("Flags"), _("Feature"),
170       _("Repair"), _("Move"), _("Window"), _("View"),
171   };
172   memcpy(cMenuNames, cMenuNames_i18n, sizeof(cMenuNames));
173 
174   const char* cMenuEntries_i18n[N_SUBMENUS][MAX_MENU_ENTRIES] = {
175       /* TRANSLATORS: This is a list of all the submenus in the map
176           editor, if the initial character is * or / then that character
177           must be perserved as it is. */
178       {_("New"), _("Open"), _("Close"), _("Save"), _("Exit"), _("Test level"), NULL},
179       {_("Edit height"), _("Edit color"), _("Edit water"), _("Edit velocity"), _("Edit lines"),
180        _("*<SPACE> Whole cell"), _("Upper corner"), _("Bottom corner"), _("Right corner"),
181        _("Left corner"), _("Center"), _("Raise increment"), _("Lower increment"),
182        _("*<SHIFT> reversed"), _("*<CTRL> walls"), NULL},
183       {_("Inc. red"), _("Inc. green"), _("Inc. blue"), _("Inc. alpha"), NULL},
184       {_("Ice"), _("Acid"), _("Sand"), _("Kill"), _("Bounce"), _("No grid"), _("Track"),
185        _("Shade flat"), _("Texture"), _("ch. texture"), NULL},
186       {_("Spike"), _("Small hill"), _("Medium hill"), _("Large hill"), _("Huge hill"),
187        _("Smooth small"), _("Smooth large"), NULL},
188       {_("Cell cont."), _("Water cont."), _("Color cont."), _("Cell center"),
189        _("Water center"), _("Color center"), _("Cell round"), _("Water round"),
190        _("Color round"), _("Vel. round"), NULL},
191       {_("/UP Move up"), _("/DOWN Move down"), _("/LEFT Move left"), _("/RIGHT Move right"),
192        "/", _("Shift map up"), _("Shift map down"), _("Shift map left"), _("Shift map right"),
193        _("*<SHIFT> x5"), "/", _("Set region marker"), _("Clear marker"), _("Copy region"),
194        _("Paste region"), NULL},
195       {_("Editor"), _("Toolbar"), _("Status"), NULL},
196       {_("Birds's eye"), _("Rotate view"), _("Load entities"), _("Clear entities"), NULL},
197   };
198   memcpy(cMenuEntries, cMenuEntries_i18n, sizeof(cMenuEntries));
199 
200   char* flagNames_i18n[NUM_FLAGS] = {_("Ice"),   _("Acid"),       _("Sand"),
201                                      _("Kill"),  _("Trampoline"), _("No grid"),
202                                      _("Track"), _("Shade flat")};
203   memcpy(flagNames, flagNames_i18n, sizeof(flagNames));
204 
205   char* hillNames_i18n[N_HILLS] = {_("Spike"),       _("Small hill"), _("Medium hill"),
206                                    _("Large hill"),  _("Huge hill"),  _("Small smooth"),
207                                    _("Large smooth")};
208   memcpy(hillNames, hillNames_i18n, sizeof(hillNames));
209 }
210 
init()211 EditMode* EditMode::init() {
212   if (!editMode) {
213     loadStrings();
214     editMode = new EditMode;
215   }
216   return editMode;
217 }
cleanup()218 void EditMode::cleanup() {
219   if (editMode) delete editMode;
220 }
221 
EditMode()222 EditMode::EditMode() {
223   map = NULL;
224   game = NULL;
225   memset(levelname, 0, sizeof(levelname));
226   mapIsWritable = 0;
227 
228   /* Load the selected level if we start with one selected */
229   if (Settings::settings->doSpecialLevel) loadMap(Settings::settings->specialLevel);
230 
231   /* Some default values for parameters in the edit mode */
232   scale = 0.5;
233   rotation = 0.0;
234   raise = 0.5;
235   color = Color(0.9, 0.9, 0.9, 1.0);
236   doSave = 0;
237   cellClipboard = NULL;
238 
239   /* Setup the windows */
240   menuWindow = new EMenuWindow();
241   statusWindow = new EStatusWindow();
242   quitWindow = new EQuitWindow();
243   saveWindow = new ESaveWindow();
244   closeWindow = new ECloseWindow();
245   openWindow = new EOpenWindow();
246   newWindow = new ENewWindow();
247   currentEditMode = editModeHeight;
248   resizeWindows();
249 
250   /* Save a reference to self in both in the class variable. */
251   EditMode::editMode = this;
252 }
253 
~EditMode()254 EditMode::~EditMode() {
255   if (map) saveMap();
256   if (cellClipboard) delete[] cellClipboard;
257   /* TODO. Delete all windows here */
258 }
259 
loadMap(char * name)260 void EditMode::loadMap(char* name) {
261   char mapname[512];
262 
263   if (map) closeMap();
264 
265   // Store name of level to edit
266   strncpy(levelname, name, sizeof(levelname));
267   levelname[255] = '\0';
268 
269   /* Set the pathname under which we will save the map */
270   snprintf(pathname, sizeof(pathname), "%s/levels/%s.map", effectiveLocalDir, name);
271 
272   // Default load the map from the home directory if existing (same as default pathname)
273   snprintf(mapname, sizeof(mapname), "%s/levels/%s.map", effectiveLocalDir, name);
274 
275   if (!fileExists(mapname))
276     // Alternativly from the share directory
277     snprintf(mapname, sizeof(mapname) - 1, "%s/levels/%s.map", effectiveShareDir, name);
278 
279   /* Note. not a problem here even if the map does not exists, it will use default values
280    * instead */
281   map = new Map(mapname);
282   map->flags |= Map::flagFlashCenter;
283   x = (int)map->startPosition[0];
284   y = (int)map->startPosition[1];
285   game = NULL;
286 
287   mapIsWritable = 1;  // Best guess for now
288 }
289 
closeMap()290 void EditMode::closeMap() {
291   if (map) {
292     delete map;
293     map = NULL;
294   }
295   if (game) {
296     delete game;
297     game = NULL;
298   }
299 }
300 
saveMap()301 void EditMode::saveMap() {
302   if (!map) return;
303 
304   char mapname[768];
305   char str[768];
306 
307   snprintf(str, sizeof(str) - 1, "%s/levels", effectiveLocalDir);
308   if (pathIsLink(str)) {
309     warning("Error, %s/levels is a symbolic link. Cannot save map", effectiveLocalDir);
310     return;
311   } else if (!pathIsDir(str))
312     mkdir(str, S_IXUSR | S_IRUSR | S_IWUSR | S_IXGRP | S_IRGRP | S_IWGRP);
313 
314   snprintf(mapname, sizeof(mapname) - 1, "%s/levels/%s.map", effectiveLocalDir, levelname);
315   if (pathIsLink(str)) {
316     warning("Error, %s/levels/%s.map is a symbolic link. Cannot save map", effectiveLocalDir,
317             levelname);
318     return;
319   }
320 
321   map->save(mapname, x, y);
322   doSave = 0;
323   doAskSave = 0;
324 
325   /* Check if there already exists a script file for this map */
326   snprintf(str, sizeof(str), "%s/levels/%s.scm", effectiveShareDir, levelname);
327   if (!pathIsFile(str)) {
328     snprintf(str, sizeof(str), "%s/levels/%s.scm", effectiveLocalDir, levelname);
329     if (!pathIsFile(str)) {
330       /* No script file exists. Create a default one */
331       if (pathIsLink(str)) {
332         warning("Error, %s is a symbolic link. Cannot create default script file", str);
333         return;
334       }
335       printf("Creating default script file at: %s\n", str);
336       FILE* fp = fopen(str, "wb");
337       fprintf(fp, ";;; %s %s\n", _("Track:"), levelname);
338       fprintf(fp, ";;; %s\n", _("This is the default script file for this track."));
339       fprintf(fp, ";;; %s\n",
340               _("Read the documentation for the editor and look at the examples"));
341       fprintf(fp, ";;; %s\n\n", _("to learn how to customize it"));
342       fprintf(fp, "(set-track-name \"%s\")\n", levelname);
343       fprintf(fp, "(set-author \"%s\")\n      ;; %s\n", username, _("Enter your name here"));
344       fprintf(fp, "(start-time 180)\n");
345       fprintf(fp, "(set-start-position 250.5 250.5)\n");
346       fprintf(fp, "(add-goal 249.0 250.0 #f \"\") ;; %s\n",
347               _("Add the name of the next level here"));
348       fprintf(fp, "(add-flag 248.0 250.0 50 #t 0.1)\n");
349       fclose(fp);
350     }
351   }
352 }
353 
display()354 void EditMode::display() {
355   double h;
356   if (map) {
357     h = map->cell(x, y).heights[Cell::CENTER];
358   } else
359     h = 0.0;
360 
361   /* Show extra cell flag overlay */
362   activeView.show_flag_state = true;
363 
364   /* configure lighting */
365   activeView.fog_enabled = 0;
366   MainMode::setupLighting(NULL, false);
367 
368   /* Setup matrixes for the camera perspective */
369   perspectiveMatrix(40, (GLdouble)screenWidth / (GLdouble)std::max(screenHeight, 1), 0.1, 200,
370                     activeView.projection);
371 
372   lookAtMatrix(x - viewPoints[switchViewpoint][0] * 7.0,
373                y - viewPoints[switchViewpoint][1] * 7.0, (birdsEye ? 30.0 : 10.0) + h * 0.5, x,
374                y, h, 0.0, 0.0, 1.0, activeView.modelview);
375 
376   activeView.day_mode = true;
377   markViewChanged();
378   /* Shadow map rendering returns active modelview/projection to orig state */
379   if (map) {
380     if (Settings::settings->doShadows) {
381       Coord3d focus((double)x, (double)y, map->getHeight(x, y));
382       renderShadowCascade(focus, map, game);
383       renderDummyShadowMap();
384     } else {
385       renderDummyShadowCascade();
386       renderDummyShadowMap();
387     }
388   }
389 
390   /* Some standard GL settings needed */
391   glEnable(GL_CULL_FACE);
392   glEnable(GL_DEPTH_TEST);
393   glDepthFunc(GL_LEQUAL);
394   glViewport(0, 0, screenWidth, screenHeight);
395   glClearColor(0., 0., 0., 1.);
396   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
397 
398   /* Draw the map and the current mapcursor/selected region */
399   if (map) {
400     Coord3d map_focus((Real)x, (Real)y, map->cell(x, y).heights[Cell::CENTER]);
401     map->draw(0, map_focus, time);
402     map->drawLoop(0, 0, map->width - 1, map->height - 1, 0);
403     if (game) {
404       /* Indicate start position and trigger object locations */
405       for (int i = 0; i < game->hooks[Role_GameHook].size(); i++) {
406         GameHook* hook = game->hooks[Role_GameHook][i];
407         if (hook->invalid) continue;
408 
409         SmartTrigger* smart_trigger = dynamic_cast<SmartTrigger*>(hook);
410         Trigger* dumb_trigger = dynamic_cast<Trigger*>(hook);
411         if (smart_trigger) {
412           map->drawSpotRing(smart_trigger->x, smart_trigger->y, smart_trigger->radius, 2);
413         }
414         if (dumb_trigger) {
415           map->drawSpotRing(dumb_trigger->x, dumb_trigger->y, dumb_trigger->radius, 1);
416         }
417       }
418 
419       map->drawSpotRing(map->startPosition[0], map->startPosition[1], 0.5, 0);
420       game->draw();
421     }
422     map->draw(1, map_focus, time);
423     activeView.show_flag_state = false;
424 
425     /* If we have a clipboard selection then display where it will be pasted */
426     if (cellClipboard) {
427       map->drawFootprint(x, y, x + cellClipboardWidth - 1, y + cellClipboardHeight - 1, 1);
428     } else if (markX >= 0) {
429       /* Otherwise, if we have a marked region then display footprint over it */
430       map->drawFootprint(std::min(markX, x), std::min(markY, y), std::max(markX, x),
431                          std::max(markY, y), 0);
432     } else if (currentEditMode == editModeFeatures) {
433       /* We have currently in the "features" edit mode, display foot
434          print of selected feature */
435       int footprintX, footprintY;
436       switch (currentFeature) {
437       case FEATURE_SPIKE:
438         footprintX = 2;
439         footprintY = 2;
440         break;
441       case FEATURE_SMALL_HILL:
442         footprintX = 3;
443         footprintY = 3;
444         break;
445       case FEATURE_MEDIUM_HILL:
446         footprintX = 5;
447         footprintY = 5;
448         break;
449       case FEATURE_LARGE_HILL:
450         footprintX = 7;
451         footprintY = 7;
452         break;
453       case FEATURE_HUGE_HILL:
454         footprintX = 11;
455         footprintY = 11;
456         break;
457       case FEATURE_SMALL_SMOOTH:
458         footprintX = 5;
459         footprintY = 5;
460         break;
461       case FEATURE_LARGE_SMOOTH:
462         footprintX = 11;
463         footprintY = 11;
464         break;
465       default:
466         footprintX = 1;
467         footprintY = 1;
468       }
469       map->drawFootprint(x - footprintX / 2, y - footprintY / 2, x + (footprintX + 1) / 2 - 1,
470                          y + (footprintY + 1) / 2 - 1, 0);
471     }
472     /* Finally, if no other case then just draw position of cursor */
473     else
474       map->drawFootprint(x, y, x, y, 0);
475   }
476 
477   clearSelectionAreas();
478   Enter2DMode();
479   MyWindow::drawAll();
480   drawMousePointer();
481   Leave2DMode();
482 }
483 
484 typedef enum { eInfo_Height = 0, eInfo_WaterHeight, eInfo_Textures, nInfos } eCellInfo;
485 int cellInfo = 0;
486 char* infoNames[nInfos] = {_("Cell height"), _("Cell water height"), _("Textures")};
487 
roundint(double f)488 int roundint(double f) {
489   /* std::round is C++11. */
490   return std::floor(f + 0.5);
491 }
492 
mouseDown(int state,int x,int y)493 void EditMode::mouseDown(int state, int x, int y) { MyWindow::mouseDownAll(state, x, y); }
doCommand(int command)494 void EditMode::doCommand(int command) {
495   int mouseX, mouseY;
496   int mouseState;
497   mouseState = SDL_GetMouseState(&mouseX, &mouseY);
498 
499   int shift = SDL_GetModState() & (KMOD_LSHIFT | KMOD_RSHIFT);
500   int ctrl = SDL_GetModState() & (KMOD_LCTRL | KMOD_RCTRL);
501 
502   /* File commands are handled first */
503   switch (command) {
504   case FILE_NEW:
505     askNew();
506     return;
507   case FILE_OPEN:
508     askOpen();
509     return;
510   case FILE_SAVE:
511     askSave();
512     return;
513   case FILE_EXIT:
514     askQuit();
515     return;
516   case FILE_CLOSE:
517     askClose();
518     return;
519   case FILE_TEST:
520     testLevel();
521     return;
522   }
523 
524   if (!map) /* If no map is opened only a limited subset of commands are available */
525     return;
526 
527   /* Some variables needed for manipulating the map */
528   Cell& cell = map->cell(x, y);
529   int x1, y1;
530   int xLow, xHigh, yLow, yHigh;
531   if (markX >= 0) {
532     xLow = std::min(x, markX);
533     xHigh = std::max(x, markX);
534     yLow = std::min(y, markY);
535     yHigh = std::max(y, markY);
536   } else {
537     xLow = xHigh = x;
538     yLow = yHigh = y;
539   }
540 
541   switch (command) {
542   case EDIT_HEIGHT:
543   case EDIT_COLOR:
544   case EDIT_WATER:
545   case EDIT_VELOCITY:
546   case EDIT_LINES:
547     currentEditMode = command - EDIT_HEIGHT;
548     break;
549   case EDIT_RAISE_INCREMENT:
550     scale = scale + 0.1;
551     if (scale > 5.0) scale = 5.0;
552     break;
553   case EDIT_LOWER_INCREMENT:
554     scale = scale - 0.1;
555     if (scale < 0.1) scale = 0.1;
556     break;
557 
558   case EDIT_UPPER:
559     doCellAction(CODE_CELL_N, mouseState & SDL_BUTTON_LMASK ? 0 : 1);
560     break;
561   case EDIT_BOTTOM:
562     doCellAction(CODE_CELL_S, mouseState & SDL_BUTTON_LMASK ? 0 : 1);
563     break;
564   case EDIT_LEFT:
565     doCellAction(CODE_CELL_W, mouseState & SDL_BUTTON_LMASK ? 0 : 1);
566     break;
567   case EDIT_RIGHT:
568     doCellAction(CODE_CELL_E, mouseState & SDL_BUTTON_LMASK ? 0 : 1);
569     break;
570   case EDIT_ALL:
571     doCellAction(CODE_CELL_ALL, mouseState & SDL_BUTTON_LMASK ? 0 : 1);
572     break;
573   case EDIT_CENTER:
574     doCellAction(CODE_CELL_C, mouseState & SDL_BUTTON_LMASK ? 0 : 1);
575     break;
576 
577   case COLOR_RED:
578     if (color.v[0] >= 65535)
579       color.v[0] = 0;
580     else
581       color.v[0] = std::min(65535, color.v[0] + 3277);
582     break;
583   case COLOR_GREEN:
584     if (color.v[1] >= 65535)
585       color.v[1] = 0;
586     else
587       color.v[1] = std::min(65535, color.v[1] + 3277);
588     break;
589   case COLOR_BLUE:
590     if (color.v[2] >= 65535)
591       color.v[2] = 0;
592     else
593       color.v[2] = std::min(65535, color.v[2] + 3277);
594     break;
595   case COLOR_ALPHA:
596     if (color.v[3] >= 65535)
597       color.v[3] = 0;
598     else
599       color.v[3] = std::min(65535, color.v[3] + 3277);
600     break;
601 
602   case FLAG_0:
603   case FLAG_1:
604   case FLAG_2:
605   case FLAG_3:
606   case FLAG_4:
607   case FLAG_5:
608   case FLAG_6:
609   case FLAG_7: {
610     int flag = command == FLAG_7 ? 1 << 11 : 1 << (command - FLAG_0);
611     int onoff = cell.flags & flag ? 0 : flag;
612     for (x1 = xLow; x1 <= xHigh; x1++)
613       for (y1 = yLow; y1 <= yHigh; y1++) {
614         Cell& c2 = map->cell(x1, y1);
615         c2.flags = (c2.flags & ~flag) | onoff;
616       }
617     map->markCellsUpdated(xLow, yLow, xHigh, yHigh, false);
618   } break;
619   case FLAG_CH_TEXTURE: {
620     int newTexture = mymod(cell.texture + 1 + (shift ? -1 : +1), numTextures + 1) - 1;
621     for (x1 = xLow; x1 <= xHigh; x1++)
622       for (y1 = yLow; y1 <= yHigh; y1++) { map->cell(x1, y1).texture = newTexture; }
623     map->markCellsUpdated(xLow, yLow, xHigh, yHigh, false);
624   } break;
625 
626   case FEATURE_SPIKE:
627   case FEATURE_SMALL_HILL:
628   case FEATURE_MEDIUM_HILL:
629   case FEATURE_LARGE_HILL:
630   case FEATURE_HUGE_HILL:
631   case FEATURE_SMALL_SMOOTH:
632   case FEATURE_LARGE_SMOOTH:
633     currentEditMode = editModeFeatures;
634     currentFeature = command;
635     break;
636 
637   case REPAIR_CELL_CONT:
638   case REPAIR_WATER_CONT:
639   case REPAIR_COLOR_CONT: {
640     /* given four cells at these relative positions, seq. corners match up */
641     int dir[4][2] = {{1, 1}, {1, 0}, {0, 1}, {0, 0}};
642 
643     int wx = (xHigh - xLow + 1), wy = (yHigh - yLow + 1);
644     float* hbuf = new float[wx * wy * 4];
645     for (int p = 0; p < (command == REPAIR_COLOR_CONT ? 4 : 1); p++) {
646       /* Read changes into a buffer */
647       for (x1 = xLow; x1 <= xHigh; x1++)
648         for (y1 = yLow; y1 <= yHigh; y1++) {
649           /* continuity enforces that corners match the neighbor average,
650            * or internal average if there are no neighbors*/
651           for (int k = 0; k < 4; k++) {
652             float external = 0.;
653             float internal = 0.;
654             int extc = 0;
655             for (int m = 0; m < 4; m++) {
656               int altx = x1 - dir[k][0] + dir[m][0], alty = y1 - dir[k][1] + dir[m][1];
657               float val;
658               switch (command) {
659               default:
660               case REPAIR_CELL_CONT:
661                 val = map->cell(altx, alty).heights[m];
662                 break;
663               case REPAIR_WATER_CONT:
664                 val = map->cell(altx, alty).waterHeights[m];
665                 break;
666               case REPAIR_COLOR_CONT:
667                 val = map->cell(altx, alty).colors[m].v[p];
668               }
669               if (xLow <= altx && altx <= xHigh && yLow <= alty && alty <= yHigh) {
670                 internal += val;
671               } else {
672                 external += val;
673                 extc++;
674               }
675             }
676             float target;
677             if (extc == 0) {
678               target = internal / 4;
679             } else {
680               target = external / extc;
681             }
682             hbuf[4 * ((x1 - xLow) * wy + (y1 - yLow)) + k] = target;
683           }
684         }
685       /* Apply changes to cells */
686       for (x1 = xLow; x1 <= xHigh; x1++)
687         for (y1 = yLow; y1 <= yHigh; y1++) {
688           Cell& c = map->cell(x1, y1);
689           float dcent = 0;
690           for (int k = 0; k < 4; k++) {
691             float target = hbuf[4 * ((x1 - xLow) * wy + (y1 - yLow)) + k];
692             switch (command) {
693             default:
694             case REPAIR_CELL_CONT:
695               dcent += target - c.heights[k];
696               c.heights[k] = target;
697               break;
698             case REPAIR_WATER_CONT:
699               dcent += target - c.waterHeights[k];
700               c.waterHeights[k] = target;
701               break;
702             case REPAIR_COLOR_CONT:
703               dcent += target - c.colors[k].v[p];
704               c.colors[k].v[p] = target;
705             }
706           }
707           switch (command) {
708           default:
709           case REPAIR_CELL_CONT:
710             c.heights[4] += dcent / 4;
711             break;
712           case REPAIR_WATER_CONT:
713             c.waterHeights[4] += dcent / 4;
714             break;
715           case REPAIR_COLOR_CONT:
716             c.colors[4].v[p] += dcent / 4;
717           }
718         }
719     }
720     delete[] hbuf;
721     map->markCellsUpdated(xLow, yLow, xHigh, yHigh, command == REPAIR_CELL_CONT);
722   } break;
723   case REPAIR_CELL_CENTERS:
724   case REPAIR_WATER_CENTERS:
725   case REPAIR_COLOR_CENTERS:
726   case REPAIR_CELL_ROUND:
727   case REPAIR_WATER_ROUND:
728   case REPAIR_COLOR_ROUND:
729   case REPAIR_VEL_ROUND: {
730     for (x1 = xLow; x1 <= xHigh; x1++)
731       for (y1 = yLow; y1 <= yHigh; y1++) {
732         Cell& c = map->cell(x1, y1);
733         switch (command) {
734         /* cell center repair sets the center as average of corners */
735         case REPAIR_CELL_CENTERS:
736           c.heights[Cell::CENTER] =
737               0.25 * (c.heights[0] + c.heights[1] + c.heights[2] + c.heights[3]);
738           break;
739         case REPAIR_WATER_CENTERS:
740           c.waterHeights[Cell::CENTER] = 0.25 * (c.waterHeights[0] + c.waterHeights[1] +
741                                                  c.waterHeights[2] + c.waterHeights[3]);
742           break;
743         case REPAIR_COLOR_CENTERS:
744           for (int k = 0; k < 4; k++) {
745             c.colors[Cell::CENTER].v[k] =
746                 (c.colors[0].v[k] + c.colors[1].v[k] + c.colors[2].v[k] + c.colors[3].v[k]) /
747                 4;
748           }
749           break;
750 
751         /* For rounding routines the central cell has 4x resolution */
752         case REPAIR_CELL_ROUND:
753           for (int k = 0; k < 4; k++) {
754             c.heights[k] = scale * roundint(c.heights[k] / scale);
755           }
756           c.heights[Cell::CENTER] =
757               0.25 * scale * roundint(4. * c.heights[Cell::CENTER] / scale);
758           break;
759         case REPAIR_WATER_ROUND:
760           for (int k = 0; k < 4; k++) {
761             c.waterHeights[k] = scale * roundint(c.waterHeights[k] / scale);
762           }
763           c.waterHeights[Cell::CENTER] =
764               0.25 * scale * roundint(4. * c.waterHeights[Cell::CENTER] / scale);
765           break;
766         case REPAIR_COLOR_ROUND:
767           for (int m = 0; m < 4; m++) {
768             float mscale = 0.05 * 65535.f;
769             for (int k = 0; k < 4; k++) {
770               c.colors[k].v[m] = mscale * roundint(c.colors[k].v[m] / mscale);
771             }
772             c.colors[Cell::CENTER].v[m] =
773                 0.25 * mscale * roundint(4. * c.colors[Cell::CENTER].v[m] / mscale);
774           }
775           break;
776         case REPAIR_VEL_ROUND:
777           c.velocity[0] = scale * roundint(c.velocity[0] / scale);
778           c.velocity[1] = scale * roundint(c.velocity[1] / scale);
779           break;
780         }
781       }
782     map->markCellsUpdated(xLow, yLow, xHigh, yHigh, command == REPAIR_CELL_ROUND);
783   } break;
784 
785   case MOVE_UP:
786     ((switchViewpoint % 2) ? y : x) +=
787         viewPoints[switchViewpoint][1] * (ctrl ? 20 : (shift ? 5 : 1));
788     break;
789   case MOVE_DOWN:
790     ((switchViewpoint % 2) ? y : x) -=
791         viewPoints[switchViewpoint][1] * (ctrl ? 20 : (shift ? 5 : 1));
792     break;
793   case MOVE_LEFT:
794     ((switchViewpoint % 2) ? x : y) +=
795         viewPoints[switchViewpoint][0] * (ctrl ? 20 : (shift ? 5 : 1));
796     break;
797   case MOVE_RIGHT:
798     ((switchViewpoint % 2) ? x : y) -=
799         viewPoints[switchViewpoint][0] * (ctrl ? 20 : (shift ? 5 : 1));
800     break;
801 
802   case MOVE_SET_MARKER:
803     markX = x;
804     markY = y;
805     break;
806   case MOVE_CLEAR_MARKER:
807     markX = -1;
808     if (cellClipboard) delete[] cellClipboard;
809     cellClipboard = NULL;
810     break;
811   case MOVE_COPY_REGION:
812     copyRegion();
813     markX = -1;
814     break;
815   case MOVE_PASTE_REGION:
816     markX = -1;
817     pasteRegion();
818     break;
819 
820   case WINDOW_EDITOR:
821   case WINDOW_TOOLBAR:
822   case WINDOW_STATUS:
823     break;
824 
825   case VIEW_BIRD:
826     birdsEye = birdsEye ? 0 : 1;
827     break;
828   case VIEW_ROTATE:
829     switchViewpoint = (switchViewpoint + 1) % 4;
830     break;
831   case VIEW_CLEAR_ENTITIES:
832     if (game) delete game;
833     game = NULL;
834     break;
835   case VIEW_LOAD_ENTITIES: {
836     /* reset entities and state */
837     if (game) delete game;
838     game = new Game(map, levelname);
839   } break;
840 
841   default:
842     /* TODO. Not implemented yet? */
843     warning("Command %d not yet implemented", command);
844     break;
845   }
846 }
847 
doCellAction(int code,int direction)848 void EditMode::doCellAction(int code, int direction) {
849   int corner;
850   int xLow, xHigh, yLow, yHigh;
851   int i, j, x1, y1;
852 
853   int ctrl = SDL_GetModState() & (KMOD_LCTRL | KMOD_RCTRL);
854   int shift = SDL_GetModState() & (KMOD_LSHIFT | KMOD_RSHIFT);
855 
856   /* Figure out which cell corner this is */
857   switch (code) {
858   case CODE_CELL_N:
859     corner = Cell::NORTH + Cell::EAST;
860     break;
861   case CODE_CELL_E:
862     corner = Cell::EAST + Cell::SOUTH;
863     break;
864   case CODE_CELL_S:
865     corner = Cell::SOUTH + Cell::WEST;
866     break;
867   case CODE_CELL_W:
868     corner = Cell::WEST + Cell::NORTH;
869     break;
870   case CODE_CELL_C:
871     corner = Cell::CENTER;
872     break;
873   default:
874     corner = Cell::CENTER;
875   }
876 
877   if (markX >= 0) {
878     xLow = std::min(x, markX);
879     xHigh = std::max(x, markX);
880     yLow = std::min(y, markY);
881     yHigh = std::max(y, markY);
882   } else {
883     xLow = xHigh = x;
884     yLow = yHigh = y;
885   }
886   if (!map) return;
887   Cell& c = map->cell(x, y);
888 
889   switch (currentEditMode) {
890   case editModeHeight:
891     for (x1 = xLow; x1 <= xHigh; x1++)
892       for (y1 = yLow; y1 <= yHigh; y1++) {
893         Cell& c2 = map->cell(x1, y1);
894         if (code == CODE_CELL_ALL) {
895           for (i = 0; i < 5; i++) c2.heights[i] += (direction ? -scale : scale);
896         } else {
897           c2.heights[corner] += (direction ? -scale : scale);
898           c2.heights[Cell::CENTER] += (direction ? -scale : scale) / 4;
899         }
900       }
901     map->markCellsUpdated(xLow, yLow, xHigh, yHigh, true);
902     break;
903   case editModeColor:
904     for (x1 = xLow; x1 <= xHigh; x1++)
905       for (y1 = yLow; y1 <= yHigh; y1++) {
906         Cell& c2 = map->cell(x1, y1);
907 
908         if (direction) {
909           /* Pick up color */
910           if (ctrl)
911             color = c.wallColors[corner];
912           else
913             color = c.colors[corner];
914         } else if (code == CODE_CELL_ALL) {
915           /* Paint color */
916           if (ctrl)
917             for (j = 0; j < 4; j++) c2.wallColors[j] = color;
918           else
919             for (j = 0; j < 5; j++) c2.colors[j] = color;
920         } else {
921           /* Paint color */
922           if (ctrl)
923             c2.wallColors[corner] = color;
924           else
925             c2.colors[corner] = color;
926         }
927       }
928     map->markCellsUpdated(xLow, yLow, xHigh, yHigh, false);
929     break;
930   case editModeWater:
931     for (x1 = xLow; x1 <= xHigh; x1++)
932       for (y1 = yLow; y1 <= yHigh; y1++) {
933         Cell& c2 = map->cell(x1, y1);
934 
935         /* Fix for incorrectly initialized water heights */
936         if (c2.waterHeights[corner] <= -100.0) {
937           c2.waterHeights[corner] = c.heights[corner];
938           c2.waterHeights[Cell::CENTER] = c.heights[Cell::CENTER];
939         }
940 
941         if (code == CODE_CELL_ALL) {
942           for (i = 0; i < 5; i++) c2.waterHeights[i] += (direction ? -scale : scale);
943         } else {
944           c2.waterHeights[corner] += direction ? -scale : scale;
945           c2.waterHeights[Cell::CENTER] += (direction ? -scale : scale) / 4;
946         }
947       }
948     map->markCellsUpdated(xLow, yLow, xHigh, yHigh, false);
949     break;
950 
951   case editModeVelocity:
952     for (x1 = xLow; x1 <= xHigh; x1++)
953       for (y1 = yLow; y1 <= yHigh; y1++) {
954         Cell& c2 = map->cell(x1, y1);
955         if (code == CODE_CELL_N) c2.velocity[1] += direction ? 0.5 : 0.1;
956         if (code == CODE_CELL_S) c2.velocity[1] -= direction ? 0.5 : 0.1;
957         if (code == CODE_CELL_W) c2.velocity[0] -= direction ? 0.5 : 0.1;
958         if (code == CODE_CELL_E) c2.velocity[0] += direction ? 0.5 : 0.1;
959       }
960     map->markCellsUpdated(xLow, yLow, xHigh, yHigh, false);
961     break;
962 
963   case editModeNoLines: {
964     int flag = 0;
965     if (code == CODE_CELL_N) flag = CELL_NOLINENORTH;
966     if (code == CODE_CELL_S) flag = CELL_NOLINESOUTH;
967     if (code == CODE_CELL_E) flag = CELL_NOLINEEAST;
968     if (code == CODE_CELL_W) flag = CELL_NOLINEWEST;
969     int onoff = c.flags & flag ? 0 : flag;
970     for (x1 = xLow; x1 <= xHigh; x1++)
971       for (y1 = yLow; y1 <= yHigh; y1++) {
972         Cell& c2 = map->cell(x1, y1);
973         c2.flags = (c2.flags & ~flag) | onoff;
974       }
975     map->markCellsUpdated(xLow, yLow, xHigh, yHigh, false);
976   } break;
977 
978   case editModeFeatures: {
979     switch (currentFeature) {
980     case FEATURE_SPIKE:
981       map->cell(x, y).heights[Cell::SW] += shift ? -raise : raise;
982       map->cell(x - 1, y).heights[Cell::SE] += shift ? -raise : raise;
983       map->cell(x, y - 1).heights[Cell::NW] += shift ? -raise : raise;
984       map->cell(x - 1, y - 1).heights[Cell::NE] += shift ? -raise : raise;
985       map->markCellsUpdated(x - 1, y - 1, x, y, false);
986       break;
987     case FEATURE_SMALL_HILL:
988       map->cell(x, y).heights[Cell::CENTER] += (shift ? -raise : raise) * 1.2;
989       for (i = 0; i < 4; i++) map->cell(x, y).heights[i] += (shift ? -raise : raise) * 1.0;
990       map->cell(x, y + 1).heights[Cell::SE] += (shift ? -raise : raise) * 1.0;
991       map->cell(x, y + 1).heights[Cell::SW] += (shift ? -raise : raise) * 1.0;
992       map->cell(x, y - 1).heights[Cell::NE] += (shift ? -raise : raise) * 1.0;
993       map->cell(x, y - 1).heights[Cell::NW] += (shift ? -raise : raise) * 1.0;
994       map->cell(x + 1, y).heights[Cell::SW] += (shift ? -raise : raise) * 1.0;
995       map->cell(x + 1, y).heights[Cell::NW] += (shift ? -raise : raise) * 1.0;
996       map->cell(x - 1, y).heights[Cell::SE] += (shift ? -raise : raise) * 1.0;
997       map->cell(x - 1, y).heights[Cell::NE] += (shift ? -raise : raise) * 1.0;
998       map->cell(x + 1, y + 1).heights[Cell::SW] += (shift ? -raise : raise) * 1.0;
999       map->cell(x - 1, y + 1).heights[Cell::SE] += (shift ? -raise : raise) * 1.0;
1000       map->cell(x + 1, y - 1).heights[Cell::NW] += (shift ? -raise : raise) * 1.0;
1001       map->cell(x - 1, y - 1).heights[Cell::NE] += (shift ? -raise : raise) * 1.0;
1002       map->cell(x + 1, y + 1).heights[Cell::CENTER] += (shift ? -raise : raise) * 0.25;
1003       map->cell(x - 1, y + 1).heights[Cell::CENTER] += (shift ? -raise : raise) * 0.25;
1004       map->cell(x + 1, y - 1).heights[Cell::CENTER] += (shift ? -raise : raise) * 0.25;
1005       map->cell(x - 1, y - 1).heights[Cell::CENTER] += (shift ? -raise : raise) * 0.25;
1006       map->cell(x + 1, y).heights[Cell::CENTER] += (shift ? -raise : raise) * 0.50;
1007       map->cell(x - 1, y).heights[Cell::CENTER] += (shift ? -raise : raise) * 0.50;
1008       map->cell(x, y + 1).heights[Cell::CENTER] += (shift ? -raise : raise) * 0.50;
1009       map->cell(x, y - 1).heights[Cell::CENTER] += (shift ? -raise : raise) * 0.50;
1010       map->markCellsUpdated(x - 1, y - 1, x + 1, y + 1, false);
1011       break;
1012     case FEATURE_MEDIUM_HILL:
1013       makeHill(2);
1014       break;
1015     case FEATURE_LARGE_HILL:
1016       makeHill(3);
1017       break;
1018     case FEATURE_HUGE_HILL:
1019       makeHill(5);
1020       break;
1021     case FEATURE_SMALL_SMOOTH:
1022       doSmooth(2);
1023       break;
1024     case FEATURE_LARGE_SMOOTH:
1025       doSmooth(5);
1026       break;
1027     }
1028   } break;
1029   }
1030 }
1031 
key(int key)1032 void EditMode::key(int key) {
1033   int shift = SDL_GetModState() & KMOD_SHIFT;
1034   int mouseX, mouseY;
1035   SDL_GetMouseState(&mouseX, &mouseY);
1036   /* TODO. Send any keys to the EOpenWindow?? */
1037 
1038   if (newWindow->isAttached()) {
1039     newWindow->key(key, shift, mouseX, mouseY);
1040     return;
1041   }
1042 
1043   /* Closing */
1044   if (closeWindow->isAttached()) {
1045     if (key == 'y') closeWindow->yes();
1046     if (key == 'n') closeWindow->no();
1047     return;
1048   }
1049 
1050   /* Quiting */
1051   if (quitWindow->isAttached()) {
1052     if (key == 'y') quitWindow->yes();
1053     if (key == 'n') quitWindow->no();
1054     return;
1055   }
1056 
1057   /* Saving */
1058   if (saveWindow->isAttached()) {
1059     if (key == 'y') saveWindow->yes();
1060     if (key == 'n') saveWindow->no();
1061     return;
1062   }
1063 
1064   /* Handle cell actions specially */
1065   if (key == 'u') {
1066     doCellAction(CODE_CELL_N, shift ? 1 : 0);
1067     return;
1068   }
1069   if (key == 'k') {
1070     doCellAction(CODE_CELL_E, shift ? 1 : 0);
1071     return;
1072   }
1073   if (key == 'm') {
1074     doCellAction(CODE_CELL_S, shift ? 1 : 0);
1075     return;
1076   }
1077   if (key == 'h') {
1078     doCellAction(CODE_CELL_W, shift ? 1 : 0);
1079     return;
1080   }
1081   if (key == 'j') {
1082     doCellAction(CODE_CELL_C, shift ? 1 : 0);
1083     return;
1084   }
1085   if (key == ' ') {
1086     doCellAction(CODE_CELL_ALL, shift ? 1 : 0);
1087     return;
1088   }
1089 
1090   if (key == SDLK_TAB) { moveKeyboardFocus(shift); }
1091   if (key == SDLK_RETURN || key == SDLK_KP_ENTER || key == SDLK_SPACE)
1092     mouseDown(shift ? 3 : 1, -1, -1);
1093 
1094   menuWindow->key(key, shift, x, y);
1095 
1096   return;
1097 }
tick(Real td)1098 void EditMode::tick(Real td) {
1099   int x, y;
1100   tickMouse(td);
1101   Uint8 mouseState = SDL_GetMouseState(&x, &y);
1102   MyWindow::mouseAll(mouseState, x, y);
1103   MyWindow::tickAll();
1104   time += td;
1105 }
activated()1106 void EditMode::activated() {
1107   clearKeyboardFocus();
1108   menuWindow->attach();
1109   statusWindow->attach();
1110 }
deactivated()1111 void EditMode::deactivated() { MyWindow::resetWindows(); }
steps(double v)1112 double steps(double v) { return ((int)(v / 0.1)) * 0.1; }
makeHill(int radius)1113 void EditMode::makeHill(int radius) {
1114   int mx, my;
1115   int diameter = radius * 2 + 1;
1116   int shift = SDL_GetModState() & (KMOD_LSHIFT | KMOD_RSHIFT);
1117 
1118   for (mx = -radius; mx <= radius; mx++)
1119     for (my = -radius; my <= radius; my++) {
1120       map->cell(x + mx, y + my).heights[Cell::CENTER] +=
1121           ((int)(std::sin(1. * M_PI * (mx + radius + 0.5) / diameter) *
1122                  std::sin(1. * M_PI * (my + radius + 0.5) / diameter) *
1123                  (shift ? -raise : raise) * 10.)) *
1124           .1;
1125       map->cell(x + mx, y + my).heights[Cell::NE] +=
1126           ((int)(std::sin(1. * M_PI * (mx + radius + 1.0) / diameter) *
1127                  std::sin(1. * M_PI * (my + radius + 1.0) / diameter) *
1128                  (shift ? -raise : raise) * 10.)) *
1129           .1;
1130       map->cell(x + mx, y + my).heights[Cell::NW] +=
1131           ((int)(std::sin(1. * M_PI * (mx + radius) / diameter) *
1132                  std::sin(1. * M_PI * (my + radius + 1.0) / diameter) *
1133                  (shift ? -raise : raise) * 10.)) *
1134           .1;
1135       map->cell(x + mx, y + my).heights[Cell::SE] +=
1136           ((int)(std::sin(1. * M_PI * (mx + radius + 1.0) / diameter) *
1137                  std::sin(1. * M_PI * (my + radius) / diameter) * (shift ? -raise : raise) *
1138                  10.)) *
1139           .1;
1140       map->cell(x + mx, y + my).heights[Cell::SW] +=
1141           ((int)(std::sin(1. * M_PI * (mx + radius) / diameter) *
1142                  std::sin(1. * M_PI * (my + radius) / diameter) * (shift ? -raise : raise) *
1143                  10.)) *
1144           .1;
1145     }
1146   map->markCellsUpdated(x - radius, y - radius, x + radius, y + radius, false);
1147 }
resizeWindows()1148 void EditMode::resizeWindows() {
1149   menuWindow->refreshChildPositions();
1150   statusWindow->resize(screenWidth, 110);
1151   statusWindow->moveTo(0, screenHeight - 110);
1152   quitWindow->moveTo(screenWidth / 2 - 100, screenHeight / 2 - 50);
1153   saveWindow->moveTo(screenWidth / 2 - 200, screenHeight / 2 - 50);
1154   closeWindow->moveTo(screenWidth / 2 - 100, screenHeight / 2 - 50);
1155   openWindow->moveTo(screenWidth / 2 - 200, screenHeight / 2 - 150);
1156   newWindow->moveTo(screenWidth / 2 - 100, screenHeight / 2 - 50);
1157 }
1158 
doSmooth(int radius)1159 void EditMode::doSmooth(int radius) {
1160   int mx, my, i;
1161   int diameter = radius * 2 + 1;
1162   int shift = SDL_GetModState() & (KMOD_LSHIFT | KMOD_RSHIFT);
1163   double avgHeight = 0.0;
1164   int north, east;
1165 
1166   for (mx = -radius; mx <= radius; mx++)
1167     for (my = -radius; my <= radius; my++) {
1168       Cell& c1 = map->cell(x + mx, y + my);
1169       for (i = 0; i < 5; i++) avgHeight += c1.heights[i];
1170     }
1171   map->markCellsUpdated(x - radius, y - radius, x + radius, x + radius, true);
1172   avgHeight = avgHeight / (5. * diameter * diameter);
1173   for (mx = -radius; mx <= radius; mx++)
1174     for (my = -radius; my <= radius; my++) {
1175       Cell& c = map->cell(x + mx, y + my);
1176       double s1 = std::sin(1. * M_PI * (mx + radius + 0.5) / diameter) *
1177                   std::sin(1. * M_PI * (my + radius + 0.5) / diameter) *
1178                   (shift ? -raise : raise);
1179       c.heights[Cell::CENTER] = steps(c.heights[Cell::CENTER] * (1. - s1) + avgHeight * s1);
1180       for (north = 0; north < 2; north++)
1181         for (east = 0; east < 2; east++) {
1182           double s = std::sin(1. * M_PI * (my + radius + 1.0 * north) / diameter) *
1183                      std::sin(1. * M_PI * (mx + radius + 1.0 * east) / diameter) *
1184                      (shift ? -raise : raise);
1185           c.heights[Cell::NORTH * north + Cell::EAST * east] = steps(
1186               c.heights[Cell::NORTH * north + Cell::EAST * east] * (1. - s) + avgHeight * s);
1187         }
1188     }
1189   map->markCellsUpdated(x - radius, y - radius, x + radius, x + radius, true);
1190 }
copyRegion()1191 void EditMode::copyRegion() {
1192   if (markX < 0) return;
1193   int x0 = std::min(x, markX);
1194   int x1 = std::max(x, markX);
1195   int y0 = std::min(y, markY);
1196   int y1 = std::max(y, markY);
1197   int width = x1 - x0 + 1;
1198   int height = y1 - y0 + 1;
1199   if (cellClipboard) delete[] cellClipboard;
1200   cellClipboard = new Cell[width * height];
1201   cellClipboardWidth = width;
1202   cellClipboardHeight = height;
1203   for (int x = 0; x < width; x++)
1204     for (int y = 0; y < height; y++) {
1205       cellClipboard[x + y * width] = map->cell(x + x0, y + y0);
1206     }
1207 }
pasteRegion()1208 void EditMode::pasteRegion() {
1209   if (!cellClipboard) return;
1210   for (int cx = 0; cx < cellClipboardWidth; cx++)
1211     for (int cy = 0; cy < cellClipboardHeight; cy++) {
1212       Cell& toCell = map->cell(cx + x, cy + y);
1213       Cell& fromCell = cellClipboard[cx + cy * cellClipboardWidth];
1214       /* We cannot just do a memcpy here since we need to preserve displaylists and other meta
1215        * data */
1216       memcpy(toCell.velocity, fromCell.velocity, sizeof(toCell.velocity));
1217       memcpy(toCell.heights, fromCell.heights, sizeof(toCell.heights));
1218       memcpy(toCell.colors, fromCell.colors, sizeof(toCell.colors));
1219       memcpy(toCell.wallColors, fromCell.wallColors, sizeof(toCell.wallColors));
1220       memcpy(toCell.waterHeights, fromCell.waterHeights, sizeof(toCell.waterHeights));
1221       toCell.sunken = fromCell.sunken;
1222       toCell.flags = fromCell.flags;
1223       toCell.texture = fromCell.texture;
1224     }
1225   map->markCellsUpdated(x, y, x + cellClipboardWidth - 1, y + cellClipboardHeight - 1, true);
1226 }
1227 
closeAllDialogWindows()1228 void EditMode::closeAllDialogWindows() {
1229   newWindow->remove();
1230   quitWindow->remove();
1231   saveWindow->remove();
1232   closeWindow->remove();
1233   openWindow->remove();
1234 }
askQuit()1235 void EditMode::askQuit() {
1236   closeAllDialogWindows();
1237   quitWindow->attach();
1238 }
askSave()1239 void EditMode::askSave() {
1240   closeAllDialogWindows();
1241   saveWindow->attach();
1242 }
askClose()1243 void EditMode::askClose() {
1244   closeAllDialogWindows();
1245   closeWindow->attach();
1246 }
askOpen()1247 void EditMode::askOpen() {
1248   closeAllDialogWindows();
1249   openWindow->attach();
1250   openWindow->refreshMapList();
1251 }
askNew()1252 void EditMode::askNew() {
1253   closeAllDialogWindows();
1254   newWindow->attach();
1255 }
1256 
testLevel()1257 void EditMode::testLevel() {
1258   snprintf(Settings::settings->specialLevel, sizeof(Settings::settings->specialLevel), "%s",
1259            levelname);
1260   Settings::settings->doSpecialLevel = 1;
1261   saveMap();
1262   GameMode::activate(SetupMode::init());
1263 }
1264