X-Git-Url: https://git.artsoft.org/?p=rocksndiamonds.git;a=blobdiff_plain;f=src%2Feditor.c;h=9396d5eff13053a7a27003c8b14be530745224c0;hp=6a0e6be2afede472e3697a134d64e16370d5ecd6;hb=b77c449c5eb0d244bd0367746ce45da07b89e38a;hpb=64e7c54dce6ea8c063f04198c64c5057d751c928 diff --git a/src/editor.c b/src/editor.c index 6a0e6be2..9396d5ef 100644 --- a/src/editor.c +++ b/src/editor.c @@ -25,11 +25,9 @@ #define INFOTEXT_UNKNOWN_ELEMENT "unknown" -/* - ----------------------------------------------------------------------------- - screen and artwork graphic pixel position definitions - ----------------------------------------------------------------------------- -*/ +// ---------------------------------------------------------------------------- +// screen and artwork graphic pixel position definitions +// ---------------------------------------------------------------------------- // values for the control window #define ED_CTRL1_BUTTONS_HORIZ 4 // toolbox @@ -354,11 +352,9 @@ SYSIZE - INFOTEXT_YSIZE) -/* - ----------------------------------------------------------------------------- - editor gadget definitions - ----------------------------------------------------------------------------- -*/ +// ---------------------------------------------------------------------------- +// editor gadget definitions +// ---------------------------------------------------------------------------- enum { @@ -646,6 +642,8 @@ enum 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, @@ -949,6 +947,8 @@ enum 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, @@ -1079,11 +1079,9 @@ enum #define ED_DRAWING_ID_EDITOR_LAST ED_DRAWING_ID_RANDOM_BACKGROUND -/* - ----------------------------------------------------------------------------- - some internally used definitions - ----------------------------------------------------------------------------- -*/ +// ---------------------------------------------------------------------------- +// some internally used definitions +// ---------------------------------------------------------------------------- // values for CopyLevelToUndoBuffer() #define UNDO_IMMEDIATE 0 @@ -1138,11 +1136,9 @@ enum #define DEFAULT_EDITOR_TILESIZE_MM TILESIZE -/* - ----------------------------------------------------------------------------- - some internally used data structure definitions - ----------------------------------------------------------------------------- -*/ +// ---------------------------------------------------------------------------- +// some internally used data structure definitions +// ---------------------------------------------------------------------------- static struct { @@ -1154,8 +1150,8 @@ static struct char shortcut; } controlbutton_info[ED_NUM_CTRL_BUTTONS] = { - /* note: some additional characters are already reserved for "cheat mode" - shortcuts (":XYZ" style) -- for details, see "events.c" */ + // note: some additional characters are already reserved for "cheat mode" + // shortcuts (":XYZ" style) -- for details, see "events.c" // ---------- toolbox control buttons --------------------------------------- @@ -3076,10 +3072,24 @@ static struct }, { 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, - "exit level if all fields solved", "automatically finish Sokoban levels" + "exit level if all tasks solved", "automatically finish Sokoban levels" }, { ED_ELEMENT_SETTINGS_XPOS(0), ED_ELEMENT_SETTINGS_YPOS(14), @@ -3747,11 +3757,9 @@ static struct }; -/* - ----------------------------------------------------------------------------- - some internally used variables - ----------------------------------------------------------------------------- -*/ +// ---------------------------------------------------------------------------- +// some internally used variables +// ---------------------------------------------------------------------------- // maximal size of level editor drawing area static int MAX_ED_FIELDX, MAX_ED_FIELDY; @@ -3788,6 +3796,8 @@ static int use_permanent_palette = TRUE; #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); @@ -5419,11 +5429,9 @@ editor_elements_info[] = }; -/* - ----------------------------------------------------------------------------- - functions - ----------------------------------------------------------------------------- -*/ +// ---------------------------------------------------------------------------- +// functions +// ---------------------------------------------------------------------------- static int getMaxInfoTextLength(void) { @@ -6682,9 +6690,9 @@ static void CreateSelectboxGadgets(void) if (selectbox_info[i].size == -1) // dynamically determine size { - /* (we cannot use -1 for uninitialized values if we directly compare - with results from strlen(), because the '<' and '>' operation will - implicitely cast -1 to an unsigned integer value!) */ + // (we cannot use -1 for uninitialized values if we directly compare + // with results from strlen(), because the '<' and '>' operation will + // implicitely cast -1 to an unsigned integer value!) selectbox_info[i].size = 0; for (j = 0; selectbox_info[i].options[j].text != NULL; j++) @@ -7138,7 +7146,10 @@ void CreateLevelEditorGadgets(void) 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; @@ -9943,10 +9954,22 @@ static void DrawPropertiesConfig(void) 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); @@ -11769,8 +11792,8 @@ static void DrawArcExt(int from_x, int from_y, int to_x2, int to_y2, radius = (int)(sqrt((float)(len_x * len_x + len_y * len_y)) + 0.5); - /* not optimal (some points get drawn twice) but simple, - and fast enough for the few points we are drawing */ + // not optimal (some points get drawn twice) but simple, + // and fast enough for the few points we are drawing for (x = 0; x <= radius; x++) { @@ -11877,12 +11900,23 @@ static void SelectArea(int from_x, int from_y, int to_x, int to_y, } // 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) { @@ -11895,26 +11929,31 @@ static void CopyBrushExt(int from_x, int from_y, int to_x, int to_y, 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; @@ -11923,13 +11962,176 @@ static void CopyBrushExt(int from_x, int from_y, int to_x, int to_y, 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); + } + + 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; + } } + } - printf("\n"); + 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; @@ -11987,6 +12189,7 @@ static void CopyBrushExt(int from_x, int from_y, int to_x, int to_y, !IN_LEV_FIELD(cursor_x + level_xpos, cursor_y + level_ypos)) { delete_old_brush = FALSE; + return; } @@ -12024,8 +12227,15 @@ static void CopyBrushExt(int from_x, int from_y, int to_x, int to_y, 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, @@ -12044,6 +12254,11 @@ static void CopyBrushToCursor(int x, int 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); @@ -12059,6 +12274,21 @@ void DumpBrush_Small(void) 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); @@ -12149,6 +12379,8 @@ static int DrawLevelText(int sx, int sy, char letter, int mode) 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; @@ -12184,7 +12416,7 @@ static int DrawLevelText(int sx, int sy, char letter, int mode) 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); @@ -12193,6 +12425,7 @@ static int DrawLevelText(int sx, int sy, char letter, int mode) case TEXT_END: CopyLevelToUndoBuffer(UNDO_IMMEDIATE); DrawEditorElement(sx, sy, Feld[lx][ly]); + StopTextInput(); typing = FALSE; break; @@ -13571,8 +13804,8 @@ static void HandleControlButtons(struct GadgetInfo *gi) case GADGET_ID_SAVE: { - /* saving read-only levels into personal level set modifies global vars - "leveldir_current" and "level_nr"; restore them after saving level */ + // saving read-only levels into personal level set modifies global vars + // "leveldir_current" and "level_nr"; restore them after saving level LevelDirTree *leveldir_former = leveldir_current; int level_nr_former = level_nr; char *level_filename; @@ -14113,6 +14346,9 @@ static void HandleDrawingAreaInfo(struct GadgetInfo *gi) else DeleteBrushFromCursor(); } + + if (!draw_with_brush) + UpdateBrushPosition(sx, sy); } else if (actual_drawing_function == GADGET_ID_PICK_ELEMENT) {