#define ED_ELEMENTLIST_YPOS (editor.palette.y)
#define ED_ELEMENTLIST_XSIZE (graphic_info[IMG_EDITOR_PALETTE_BUTTON].width)
#define ED_ELEMENTLIST_YSIZE (graphic_info[IMG_EDITOR_PALETTE_BUTTON].height)
-#define ED_ELEMENTLIST_BUTTONS_HORIZ (editor.palette.cols)
-#define ED_ELEMENTLIST_BUTTONS_VERT (editor.palette.rows)
+#define ED_ELEMENTLIST_COLS MAX(1, editor.palette.cols)
+#define ED_ELEMENTLIST_ROWS MAX(1, editor.palette.rows)
+#define ED_ELEMENTLIST_BUTTONS_HORIZ (ED_ELEMENTLIST_COLS)
+#define ED_ELEMENTLIST_BUTTONS_VERT (ED_ELEMENTLIST_ROWS)
#define ED_NUM_ELEMENTLIST_BUTTONS (ED_ELEMENTLIST_BUTTONS_HORIZ * \
ED_ELEMENTLIST_BUTTONS_VERT)
GADGET_ID_RANDOM_BALL_CONTENT,
GADGET_ID_INITIAL_BALL_STATE,
GADGET_ID_GROW_INTO_DIGGABLE,
+ GADGET_ID_SB_FIELDS_NEEDED,
+ GADGET_ID_SB_OBJECTS_NEEDED,
GADGET_ID_AUTO_EXIT_SOKOBAN,
GADGET_ID_SOLVED_BY_ONE_PLAYER,
GADGET_ID_CONTINUOUS_SNAPPING,
ED_CHECKBUTTON_ID_RANDOM_BALL_CONTENT,
ED_CHECKBUTTON_ID_INITIAL_BALL_STATE,
ED_CHECKBUTTON_ID_GROW_INTO_DIGGABLE,
+ ED_CHECKBUTTON_ID_SB_FIELDS_NEEDED,
+ ED_CHECKBUTTON_ID_SB_OBJECTS_NEEDED,
ED_CHECKBUTTON_ID_AUTO_EXIT_SOKOBAN,
ED_CHECKBUTTON_ID_SOLVED_BY_ONE_PLAYER,
ED_CHECKBUTTON_ID_CONTINUOUS_SNAPPING,
},
{
ED_ELEMENT_SETTINGS_XPOS(0), ED_ELEMENT_SETTINGS_YPOS(0),
+ GADGET_ID_SB_FIELDS_NEEDED, GADGET_ID_NONE,
+ &level.sb_fields_needed,
+ NULL, NULL,
+ "all fields need to be filled", "require all SB fields to be solved"
+ },
+ {
+ ED_ELEMENT_SETTINGS_XPOS(0), ED_ELEMENT_SETTINGS_YPOS(0),
+ GADGET_ID_SB_OBJECTS_NEEDED, GADGET_ID_NONE,
+ &level.sb_objects_needed,
+ NULL, NULL,
+ "all objects need to be placed", "require all SB objects to be solved"
+ },
+ {
+ ED_ELEMENT_SETTINGS_XPOS(0), ED_ELEMENT_SETTINGS_YPOS(1),
GADGET_ID_AUTO_EXIT_SOKOBAN, GADGET_ID_NONE,
&level.auto_exit_sokoban,
NULL, NULL,
#define PYSIZE (use_permanent_palette ? DYSIZE : SYSIZE)
// forward declaration for internal use
+static void CopyBrushToCursor(int, int);
+static void DeleteBrushFromCursor(void);
static void ModifyEditorCounterValue(int, int);
static void ModifyEditorCounterLimits(int, int, int);
static void ModifyEditorSelectboxValue(int, int);
{
// required for correct padding of palette headline buttons
if (*editor_elements_info[i].headline_list_size > 0)
- num_editor_elements += editor.palette.cols;
+ num_editor_elements += ED_ELEMENTLIST_COLS;
for (j = 0; j < *editor_elements_info[i].headline_list_size; j++)
{
// required for correct padding of palette element buttons
int element_list_size = *editor_elements_info[i].element_list_size;
int element_rows =
- (element_list_size + editor.palette.cols - 1) / editor.palette.cols;
- int element_buttons = editor.palette.cols * element_rows;
+ (element_list_size + ED_ELEMENTLIST_COLS - 1) / ED_ELEMENTLIST_COLS;
+ int element_buttons = ED_ELEMENTLIST_COLS * element_rows;
num_editor_elements += element_buttons;
}
{
// required for correct padding of palette headline buttons
int headline_size = (*editor_elements_info[i].headline_list_size > 0 ?
- editor.palette.cols : 0);
+ ED_ELEMENTLIST_COLS : 0);
for (j = 0; j < headline_size; j++)
{
// required for correct padding of palette element buttons
int element_list_size = *editor_elements_info[i].element_list_size;
int element_rows =
- (element_list_size + editor.palette.cols - 1) / editor.palette.cols;
- int element_buttons = editor.palette.cols * element_rows;
+ (element_list_size + ED_ELEMENTLIST_COLS - 1) / ED_ELEMENTLIST_COLS;
+ int element_buttons = ED_ELEMENTLIST_COLS * element_rows;
// copy all elements from element list
for (j = 0; j < element_list_size; j++)
right_gadget_border =
checked_calloc(num_editor_gadgets * sizeof(int));
- editor_el_empty = checked_calloc(ED_NUM_ELEMENTLIST_BUTTONS * sizeof(int));
+ // set number of empty (padding) element buttons to maximum number of buttons
+ num_editor_el_empty = ED_NUM_ELEMENTLIST_BUTTONS;
+
+ editor_el_empty = checked_calloc(num_editor_el_empty * sizeof(int));
editor_el_empty_ptr = editor_el_empty;
use_permanent_palette = !editor.palette.show_as_separate_screen;
return (door_1_viewport_unchanged && door_1_contains_toolbox);
}
+static void DrawEditorDoorBackground(int graphic, int x, int y,
+ int width, int height)
+{
+ struct GraphicInfo *g = &graphic_info[graphic];
+
+ if (g->bitmap != NULL)
+ BlitBitmap(g->bitmap, drawto, g->src_x, g->src_y,
+ MIN(width, g->width), MIN(height, g->height), x, y);
+ else
+ ClearRectangle(drawto, x, y, width, height);
+}
+
static void DrawEditorDoorContent(void)
{
// needed for gadgets drawn on background (like palette scrollbar)
SetDoorBackgroundImage(IMG_UNDEFINED);
// copy default editor door content to main double buffer
- BlitBitmap(graphic_info[IMG_BACKGROUND_PALETTE].bitmap, drawto,
- graphic_info[IMG_BACKGROUND_PALETTE].src_x,
- graphic_info[IMG_BACKGROUND_PALETTE].src_y,
- MIN(DXSIZE, graphic_info[IMG_BACKGROUND_PALETTE].width),
- MIN(DYSIZE, graphic_info[IMG_BACKGROUND_PALETTE].height),
- DX, DY);
+ DrawEditorDoorBackground(IMG_BACKGROUND_PALETTE, DX, DY, DXSIZE, DYSIZE);
// draw bigger door
DrawSpecialEditorDoor();
// draw new control window
- BlitBitmap(graphic_info[IMG_BACKGROUND_TOOLBOX].bitmap, drawto,
- graphic_info[IMG_BACKGROUND_TOOLBOX].src_x,
- graphic_info[IMG_BACKGROUND_TOOLBOX].src_y,
- MIN(EXSIZE, graphic_info[IMG_BACKGROUND_TOOLBOX].width),
- MIN(EYSIZE, graphic_info[IMG_BACKGROUND_TOOLBOX].height),
- EX, EY);
+ DrawEditorDoorBackground(IMG_BACKGROUND_TOOLBOX, EX, EY, EXSIZE, EYSIZE);
// draw all toolbox gadgets to editor doors
MapControlButtons();
FadeSoundsAndMusic();
- if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged())
+ if (CheckFadeAll())
fade_mask = REDRAW_ALL;
FadeOut(fade_mask);
{
int element = Feld[x][y];
- if (IS_GEM(element) ||
- element == EL_MM_KETTLE ||
- element == EL_DF_CELL)
- level.gems_needed++;
+ switch (element)
+ {
+ case EL_EMERALD:
+ case EL_EMERALD_YELLOW:
+ case EL_EMERALD_RED:
+ case EL_EMERALD_PURPLE:
+ case EL_BD_DIAMOND:
+ case EL_WALL_EMERALD:
+ case EL_WALL_EMERALD_YELLOW:
+ case EL_WALL_EMERALD_RED:
+ case EL_WALL_EMERALD_PURPLE:
+ case EL_WALL_BD_DIAMOND:
+ case EL_NUT:
+ case EL_SP_INFOTRON:
+ case EL_MM_KETTLE:
+ case EL_DF_CELL:
+ level.gems_needed++;
+ break;
+
+ case EL_DIAMOND:
+ case EL_WALL_DIAMOND:
+ level.gems_needed += 3;
+ break;
+
+ case EL_PEARL:
+ case EL_WALL_PEARL:
+ level.gems_needed += 5;
+ break;
+
+ case EL_CRYSTAL:
+ case EL_WALL_CRYSTAL:
+ level.gems_needed += 8;
+ break;
+
+ default:
+ break;
+ }
}
}
MapCheckbuttonGadget(ED_CHECKBUTTON_ID_GROW_INTO_DIGGABLE);
}
+ if (properties_element == EL_SOKOBAN_FIELD_EMPTY)
+ MapCheckbuttonGadget(ED_CHECKBUTTON_ID_SB_FIELDS_NEEDED);
+
+ if (properties_element == EL_SOKOBAN_OBJECT)
+ MapCheckbuttonGadget(ED_CHECKBUTTON_ID_SB_OBJECTS_NEEDED);
+
if (properties_element == EL_SOKOBAN_OBJECT ||
properties_element == EL_SOKOBAN_FIELD_EMPTY ||
properties_element == EL_SOKOBAN_FIELD_FULL)
+ {
+ checkbutton_info[ED_CHECKBUTTON_ID_AUTO_EXIT_SOKOBAN].y =
+ ED_ELEMENT_SETTINGS_XPOS(properties_element == EL_SOKOBAN_FIELD_FULL ?
+ 0 : 1);
+
MapCheckbuttonGadget(ED_CHECKBUTTON_ID_AUTO_EXIT_SOKOBAN);
+ }
if (IS_BALLOON_ELEMENT(properties_element))
MapSelectboxGadget(ED_SELECTBOX_ID_WIND_DIRECTION);
static boolean draw_mode_hires = FALSE;
+static boolean isHiresTileElement(int element)
+{
+ return (IS_MM_WALL(element) || element == EL_EMPTY);
+}
+
+static boolean isHiresDrawElement(int element)
+{
+ return (IS_MM_WALL_EDITOR(element) || element == EL_EMPTY);
+}
+
static void SetDrawModeHiRes(int element)
{
draw_mode_hires =
(level.game_engine_type == GAME_ENGINE_TYPE_MM &&
- (IS_MM_WALL_EDITOR(element) || element == EL_EMPTY));
+ isHiresDrawElement(element));
}
static boolean getDrawModeHiRes(void)
}
// values for CopyBrushExt()
-#define CB_AREA_TO_BRUSH 0
-#define CB_BRUSH_TO_CURSOR 1
-#define CB_BRUSH_TO_LEVEL 2
-#define CB_DELETE_OLD_CURSOR 3
-#define CB_DUMP_BRUSH 4
-#define CB_DUMP_BRUSH_SMALL 5
+#define CB_AREA_TO_BRUSH 0
+#define CB_BRUSH_TO_CURSOR 1
+#define CB_BRUSH_TO_LEVEL 2
+#define CB_DELETE_OLD_CURSOR 3
+#define CB_DUMP_BRUSH 4
+#define CB_DUMP_BRUSH_SMALL 5
+#define CB_CLIPBOARD_TO_BRUSH 6
+#define CB_BRUSH_TO_CLIPBOARD 7
+#define CB_BRUSH_TO_CLIPBOARD_SMALL 8
+#define CB_UPDATE_BRUSH_POSITION 9
+
+#define MAX_CB_PART_SIZE 10
+#define MAX_CB_LINE_SIZE (MAX_LEV_FIELDX + 1) // text plus newline
+#define MAX_CB_NUM_LINES (MAX_LEV_FIELDY)
+#define MAX_CB_TEXT_SIZE (MAX_CB_LINE_SIZE * \
+ MAX_CB_NUM_LINES * \
+ MAX_CB_PART_SIZE)
static void DrawBrushElement(int sx, int sy, int element, boolean change_level)
{
static short brush_buffer[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
static int brush_width, brush_height;
static int last_cursor_x = -1, last_cursor_y = -1;
- static boolean delete_old_brush;
+ static boolean delete_old_brush = FALSE;
int new_element = BUTTON_ELEMENT(button);
int x, y;
if (mode == CB_DUMP_BRUSH ||
- mode == CB_DUMP_BRUSH_SMALL)
+ mode == CB_DUMP_BRUSH_SMALL ||
+ mode == CB_BRUSH_TO_CLIPBOARD ||
+ mode == CB_BRUSH_TO_CLIPBOARD_SMALL)
{
- if (!draw_with_brush)
- {
- Error(ERR_WARN, "no brush selected");
-
+ if (edit_mode != ED_MODE_DRAWING)
return;
- }
- for (y = 0; y < brush_height; y++)
+ char part[MAX_CB_PART_SIZE + 1] = "";
+ char text[MAX_CB_TEXT_SIZE + 1] = "";
+ int width = (draw_with_brush ? brush_width : lev_fieldx);
+ int height = (draw_with_brush ? brush_height : lev_fieldy);
+
+ for (y = 0; y < height; y++)
{
- for (x = 0; x < brush_width; x++)
+ for (x = 0; x < width; x++)
{
- int element = brush_buffer[x][y];
+ int element = (draw_with_brush ? brush_buffer[x][y] : Feld[x][y]);
int element_mapped = element;
+ char *prefix = (mode == CB_DUMP_BRUSH ||
+ mode == CB_BRUSH_TO_CLIPBOARD ? "`" : "¸");
if (IS_CUSTOM_ELEMENT(element))
element_mapped = EL_CUSTOM_START;
else if (element >= NUM_FILE_ELEMENTS)
element_mapped = EL_UNKNOWN;
- // dump brush as level sketch text for the R'n'D forum:
+ // copy brush to level sketch text buffer for the R'n'D forum:
// - large tiles: `xxx (0x60 ASCII)
// - small tiles: ¸xxx (0xb8 ISO-8859-1, 0xc2b8 UTF-8)
- printf("%s%03d", (mode == CB_DUMP_BRUSH ? "`" : "¸"), element_mapped);
+ snprintf(part, MAX_CB_PART_SIZE + 1, "%s%03d", prefix, element_mapped);
+ strcat(text, part);
}
- printf("\n");
+ strcat(text, "\n");
+ }
+
+ if (mode == CB_BRUSH_TO_CLIPBOARD ||
+ mode == CB_BRUSH_TO_CLIPBOARD_SMALL)
+ SDL_SetClipboardText(text);
+ else
+ printf("%s", text);
+
+ return;
+ }
+
+ if (mode == CB_CLIPBOARD_TO_BRUSH)
+ {
+ if (edit_mode != ED_MODE_DRAWING)
+ return;
+
+ if (!SDL_HasClipboardText())
+ {
+ Request("Clipboard is empty!", REQ_CONFIRM);
+
+ return;
+ }
+
+ boolean copy_to_brush = (draw_with_brush ||
+ drawing_function == GADGET_ID_GRAB_BRUSH);
+
+ // this will delete the old brush, if already drawing with a brush
+ if (copy_to_brush)
+ ClickOnGadget(level_editor_gadget[GADGET_ID_SINGLE_ITEMS], MB_LEFTBUTTON);
+
+ // initialization is required for "odd" (incomplete) clipboard content
+ for (x = 0; x < MAX_LEV_FIELDX; x++)
+ for (y = 0; y < MAX_LEV_FIELDY; y++)
+ brush_buffer[x][y] = EL_EMPTY;
+
+ brush_width = 0;
+ brush_height = 0;
+ x = 0;
+ y = 0;
+
+ char *clipboard_text = SDL_GetClipboardText();
+ char *ptr = clipboard_text;
+ boolean stop = FALSE;
+
+ while (*ptr && !stop)
+ {
+ boolean prefix_found = FALSE;
+
+ // level sketch element number prefixes (may be multi-byte characters)
+ char *prefix_list[] = { "`", "¸" };
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(prefix_list); i++)
+ {
+ char *prefix = prefix_list[i];
+
+ // check if string is large enough for prefix
+ if (strlen(ptr) < strlen(prefix))
+ {
+ stop = TRUE;
+
+ break;
+ }
+
+ // check if string starts with prefix
+ if (strPrefix(ptr, prefix))
+ {
+ ptr += strlen(prefix);
+
+ prefix_found = TRUE;
+
+ break;
+ }
+ }
+
+ // continue with next character if prefix not found
+ if (!prefix_found)
+ {
+ ptr++; // !!! FIX THIS for real UTF-8 handling !!!
+
+ continue;
+ }
+
+ // continue with next character if prefix not found
+ if (strlen(ptr) < 3)
+ break;
+
+ if (ptr[0] >= '0' && ptr[0] <= '9' &&
+ ptr[1] >= '0' && ptr[1] <= '9' &&
+ ptr[2] >= '0' && ptr[2] <= '9')
+ {
+ int element = ((ptr[0] - '0') * 100 +
+ (ptr[1] - '0') * 10 +
+ (ptr[2] - '0'));
+
+ ptr += 3;
+
+ if (element >= NUM_FILE_ELEMENTS)
+ element = EL_UNKNOWN;
+
+ brush_buffer[x][y] = element;
+
+ brush_width = MAX(x + 1, brush_width);
+ brush_height = MAX(y + 1, brush_height);
+
+ x++;
+
+ if (x >= MAX_LEV_FIELDX || *ptr == '\n')
+ {
+ x = 0;
+ y++;
+
+ if (y >= MAX_LEV_FIELDY)
+ stop = TRUE;
+ }
+ }
+ }
+
+ SDL_free(clipboard_text);
+
+ if (brush_width == 0 || brush_height == 0)
+ {
+ Request("No level sketch found in clipboard!", REQ_CONFIRM);
+
+ return;
+ }
+
+ if (copy_to_brush)
+ {
+ struct GadgetInfo *gi = level_editor_gadget[GADGET_ID_DRAWING_LEVEL];
+ int mx, my;
+
+ SDL_GetMouseState(&mx, &my);
+
+ // if inside drawing area, activate and draw brush at last mouse position
+ if (mx >= gi->x && mx < gi->x + gi->width &&
+ my >= gi->y && my < gi->y + gi->height)
+ CopyBrushToCursor(last_cursor_x, last_cursor_y);
+
+ draw_with_brush = TRUE;
+ }
+ else
+ {
+ char request[100];
+
+ sprintf(request, "Replace level with %dx%d level sketch from clipboard?",
+ brush_width, brush_height);
+
+ if (!Request(request, REQ_ASK))
+ return;
+
+ for (x = 0; x < MAX_LEV_FIELDX; x++)
+ for (y = 0; y < MAX_LEV_FIELDY; y++)
+ Feld[x][y] = brush_buffer[x][y];
+
+ lev_fieldx = level.fieldx = brush_width;
+ lev_fieldy = level.fieldy = brush_height;
+
+ SetBorderElement();
+
+ DrawEditModeWindow();
+ CopyLevelToUndoBuffer(UNDO_IMMEDIATE);
}
return;
!IN_LEV_FIELD(cursor_x + level_xpos, cursor_y + level_ypos))
{
delete_old_brush = FALSE;
+
return;
}
last_cursor_x = cursor_x;
last_cursor_y = cursor_y;
+
delete_old_brush = TRUE;
}
+
+ if (mode == CB_UPDATE_BRUSH_POSITION)
+ {
+ last_cursor_x = from_x;
+ last_cursor_y = from_y;
+ }
}
static void CopyAreaToBrush(int from_x, int from_y, int to_x, int to_y,
CopyBrushExt(x, y, 0, 0, 0, CB_BRUSH_TO_CURSOR);
}
+static void UpdateBrushPosition(int x, int y)
+{
+ CopyBrushExt(x, y, 0, 0, 0, CB_UPDATE_BRUSH_POSITION);
+}
+
static void DeleteBrushFromCursor(void)
{
CopyBrushExt(0, 0, 0, 0, 0, CB_DELETE_OLD_CURSOR);
CopyBrushExt(0, 0, 0, 0, 0, CB_DUMP_BRUSH_SMALL);
}
+void CopyClipboardToBrush(void)
+{
+ CopyBrushExt(0, 0, 0, 0, 0, CB_CLIPBOARD_TO_BRUSH);
+}
+
+void CopyBrushToClipboard(void)
+{
+ CopyBrushExt(0, 0, 0, 0, 0, CB_BRUSH_TO_CLIPBOARD);
+}
+
+void CopyBrushToClipboard_Small(void)
+{
+ CopyBrushExt(0, 0, 0, 0, 0, CB_BRUSH_TO_CLIPBOARD_SMALL);
+}
+
static void FloodFill(int from_x, int from_y, int fill_element)
{
FloodFillLevel(from_x, from_y, fill_element, Feld, lev_fieldx, lev_fieldy);
case TEXT_SETCURSOR:
DrawEditorElement(last_sx, last_sy, Feld[lx][ly]);
DrawAreaBorder(sx, sy, sx, sy);
+ StartTextInput(SX + sx * ed_tilesize, SY + sy * ed_tilesize,
+ ed_tilesize, ed_tilesize);
last_sx = sx;
last_sy = sy;
break;
break;
case TEXT_NEWLINE:
- if (sy + 1 < ed_fieldy - 1 && ly + 1 < lev_fieldy - 1)
+ if (sy + 1 < ed_fieldy && ly + 1 < lev_fieldy)
DrawLevelText(start_sx, sy + 1, 0, TEXT_SETCURSOR);
else
DrawLevelText(0, 0, 0, TEXT_END);
case TEXT_END:
CopyLevelToUndoBuffer(UNDO_IMMEDIATE);
DrawEditorElement(sx, sy, Feld[lx][ly]);
+ StopTextInput();
typing = FALSE;
break;
}
else if (!button_press_event)
{
+ int old_element = (IN_LEV_FIELD(lx, ly) ? Feld[lx][ly] : EL_UNDEFINED);
+ boolean hires_drawing = (level.game_engine_type == GAME_ENGINE_TYPE_MM &&
+ isHiresTileElement(old_element) &&
+ isHiresDrawElement(new_element));
+
// prevent handling events for every pixel position when moving mouse
- if ((sx == last_sx && sy == last_sy &&
- !IS_MM_WALL_EDITOR(new_element) && new_element != EL_EMPTY) ||
+ if ((sx == last_sx && sy == last_sy && !hires_drawing) ||
(sx2 == last_sx2 && sy2 == last_sy2))
return;
}
else
DeleteBrushFromCursor();
}
+
+ if (!draw_with_brush)
+ UpdateBrushPosition(sx, sy);
}
else if (actual_drawing_function == GADGET_ID_PICK_ELEMENT)
{