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