X-Git-Url: https://git.artsoft.org/?a=blobdiff_plain;f=src%2Feditor.c;h=7931d89665ee533c75f9b6346322840ac4a597c9;hb=1a22e8bdd70708d1ee0bb3f21a456eca4e3de50a;hp=7757fe51e4053a7beb27682fba4bcb42d84c4b7f;hpb=12a8fd3a64d6bee5ca5f5b89e4a00b49d78bbd2c;p=rocksndiamonds.git diff --git a/src/editor.c b/src/editor.c index 7757fe51..7931d896 100644 --- a/src/editor.c +++ b/src/editor.c @@ -65,8 +65,10 @@ #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) @@ -640,8 +642,10 @@ enum GADGET_ID_USE_TIME_ORB_BUG, GADGET_ID_USE_LIFE_BUGS, GADGET_ID_RANDOM_BALL_CONTENT, - GADGET_ID_INITIAL_BALL_STATE, + GADGET_ID_INITIAL_BALL_ACTIVE, 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, @@ -943,8 +947,10 @@ enum ED_CHECKBUTTON_ID_USE_TIME_ORB_BUG, ED_CHECKBUTTON_ID_USE_LIFE_BUGS, ED_CHECKBUTTON_ID_RANDOM_BALL_CONTENT, - ED_CHECKBUTTON_ID_INITIAL_BALL_STATE, + ED_CHECKBUTTON_ID_INITIAL_BALL_ACTIVE, 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, @@ -1959,6 +1965,10 @@ static struct ValueTextInfo options_change_direct_action[] = #endif { CE_VALUE_GETS_ZERO, "CE value gets 0" }, { CE_SCORE_GETS_ZERO, "CE score gets 0" }, + { CE_UNDEFINED, " " }, + { CE_HEADLINE_SPECIAL_EVENTS, "[mouse events]" }, + { CE_CLICKED_BY_MOUSE, "clicked by mouse" }, + { CE_PRESSED_BY_MOUSE, "pressed by mouse" }, { -1, NULL } }; @@ -1988,6 +1998,10 @@ static struct ValueTextInfo options_change_other_action[] = { CE_SCORE_CHANGES_OF_X, "CE score changes of" }, { CE_VALUE_GETS_ZERO_OF_X, "CE value gets 0 of" }, { CE_SCORE_GETS_ZERO_OF_X, "CE score gets 0 of" }, + { CE_UNDEFINED, " " }, + { CE_HEADLINE_SPECIAL_EVENTS, "[mouse events]" }, + { CE_MOUSE_CLICKED_ON_X, "mouse clicked on" }, + { CE_MOUSE_PRESSED_ON_X, "mouse pressed on" }, { -1, NULL } }; @@ -2078,10 +2092,11 @@ static struct ValueTextInfo options_action_type[] = { CA_SET_LEVEL_SCORE, "set score" }, { CA_SET_LEVEL_GEMS, "set gems" }, { CA_SET_LEVEL_WIND, "set wind dir." }, - { CA_SET_LEVEL_RANDOM_SEED, "set rand. seed" }, + { CA_SET_LEVEL_RANDOM_SEED, "set random seed" }, { CA_UNDEFINED, " " }, { CA_HEADLINE_PLAYER_ACTIONS, "[player]" }, { CA_MOVE_PLAYER, "move player" }, + { CA_MOVE_PLAYER_NEW, "move player new" }, { CA_EXIT_PLAYER, "exit player" }, { CA_KILL_PLAYER, "kill player" }, { CA_SET_PLAYER_KEYS, "set keys" }, @@ -2407,6 +2422,7 @@ action_arg_options[] = { CA_EXIT_PLAYER, 0, options_action_arg_player, }, { CA_KILL_PLAYER, 0, options_action_arg_player, }, { CA_MOVE_PLAYER, 0, options_action_arg_direction, }, + { CA_MOVE_PLAYER_NEW, 0, options_action_arg_direction, }, { CA_RESTART_LEVEL, 0, options_action_arg_none, }, { CA_SHOW_ENVELOPE, 0, options_action_arg_envelope, }, { CA_SET_LEVEL_TIME, 3, options_action_arg_number, }, @@ -2659,7 +2675,7 @@ static struct { ED_ELEMENT_SETTINGS_XPOS(1), ED_ELEMENT_SETTINGS_YPOS(13), GADGET_ID_ACTION_TYPE, GADGET_ID_NONE, - -1, + 15, options_action_type, &custom_element_change.action_type, NULL, NULL, NULL, "action on specified condition" @@ -3054,8 +3070,8 @@ static struct }, { ED_ELEMENT_SETTINGS_XPOS(0), ED_ELEMENT_SETTINGS_YPOS(1), - GADGET_ID_INITIAL_BALL_STATE, GADGET_ID_NONE, - &level.ball_state_initial, + GADGET_ID_INITIAL_BALL_ACTIVE, GADGET_ID_NONE, + &level.ball_active_initial, NULL, NULL, "magic ball initially activated", "activate magic ball after level start" }, @@ -3068,10 +3084,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), @@ -3442,7 +3472,7 @@ static struct int gadget_id_align; int *value; int area_xsize, area_ysize; - char *text_left, *text_right, *text_below, *infotext; + char *text_left, *text_right, *text_above, *text_below, *infotext; } drawingarea_info[ED_NUM_DRAWING_AREAS] = { // ---------- level playfield content --------------------------------------- @@ -3453,7 +3483,7 @@ static struct GADGET_ID_DRAWING_LEVEL, GADGET_ID_NONE, NULL, -1, -1, // these values are not constant, but can change at runtime - NULL, NULL, NULL, NULL + NULL, NULL, NULL, NULL, NULL }, // ---------- yam yam content ----------------------------------------------- @@ -3463,56 +3493,56 @@ static struct ED_AREA_YAMYAM_CONTENT_XOFF(0), ED_AREA_YAMYAM_CONTENT_YOFF(0), GADGET_ID_YAMYAM_CONTENT_0, GADGET_ID_NONE, &level.yamyam_content[0].e[0][0], 3, 3, - NULL, NULL, "1", NULL + NULL, NULL, NULL, "1", NULL }, { ED_AREA_YAMYAM_CONTENT_XPOS, ED_AREA_YAMYAM_CONTENT_YPOS, ED_AREA_YAMYAM_CONTENT_XOFF(1), ED_AREA_YAMYAM_CONTENT_YOFF(1), GADGET_ID_YAMYAM_CONTENT_1, GADGET_ID_NONE, &level.yamyam_content[1].e[0][0], 3, 3, - NULL, NULL, "2", NULL + NULL, NULL, NULL, "2", NULL }, { ED_AREA_YAMYAM_CONTENT_XPOS, ED_AREA_YAMYAM_CONTENT_YPOS, ED_AREA_YAMYAM_CONTENT_XOFF(2), ED_AREA_YAMYAM_CONTENT_YOFF(2), GADGET_ID_YAMYAM_CONTENT_2, GADGET_ID_NONE, &level.yamyam_content[2].e[0][0], 3, 3, - NULL, NULL, "3", NULL + NULL, NULL, NULL, "3", NULL }, { ED_AREA_YAMYAM_CONTENT_XPOS, ED_AREA_YAMYAM_CONTENT_YPOS, ED_AREA_YAMYAM_CONTENT_XOFF(3), ED_AREA_YAMYAM_CONTENT_YOFF(3), GADGET_ID_YAMYAM_CONTENT_3, GADGET_ID_NONE, &level.yamyam_content[3].e[0][0], 3, 3, - NULL, NULL, "4", NULL + NULL, NULL, NULL, "4", NULL }, { ED_AREA_YAMYAM_CONTENT_XPOS, ED_AREA_YAMYAM_CONTENT_YPOS, ED_AREA_YAMYAM_CONTENT_XOFF(4), ED_AREA_YAMYAM_CONTENT_YOFF(4), GADGET_ID_YAMYAM_CONTENT_4, GADGET_ID_NONE, &level.yamyam_content[4].e[0][0], 3, 3, - NULL, NULL, "5", NULL + NULL, NULL, NULL, "5", NULL }, { ED_AREA_YAMYAM_CONTENT_XPOS, ED_AREA_YAMYAM_CONTENT_YPOS, ED_AREA_YAMYAM_CONTENT_XOFF(5), ED_AREA_YAMYAM_CONTENT_YOFF(5), GADGET_ID_YAMYAM_CONTENT_5, GADGET_ID_NONE, &level.yamyam_content[5].e[0][0], 3, 3, - NULL, NULL, "6", NULL + NULL, NULL, NULL, "6", NULL }, { ED_AREA_YAMYAM_CONTENT_XPOS, ED_AREA_YAMYAM_CONTENT_YPOS, ED_AREA_YAMYAM_CONTENT_XOFF(6), ED_AREA_YAMYAM_CONTENT_YOFF(6), GADGET_ID_YAMYAM_CONTENT_6, GADGET_ID_NONE, &level.yamyam_content[6].e[0][0], 3, 3, - NULL, NULL, "7", NULL + NULL, NULL, NULL, "7", NULL }, { ED_AREA_YAMYAM_CONTENT_XPOS, ED_AREA_YAMYAM_CONTENT_YPOS, ED_AREA_YAMYAM_CONTENT_XOFF(7), ED_AREA_YAMYAM_CONTENT_YOFF(7), GADGET_ID_YAMYAM_CONTENT_7, GADGET_ID_NONE, &level.yamyam_content[7].e[0][0], 3, 3, - NULL, NULL, "8", NULL + NULL, NULL, NULL, "8", NULL }, // ---------- magic ball content -------------------------------------------- @@ -3522,66 +3552,66 @@ static struct ED_AREA_MAGIC_BALL_CONTENT_XOFF(0), ED_AREA_MAGIC_BALL_CONTENT_YOFF(0), GADGET_ID_MAGIC_BALL_CONTENT_0, GADGET_ID_NONE, &level.ball_content[0].e[0][0], 3, 3, - NULL, NULL, "1", NULL + NULL, NULL, NULL, "1", NULL }, { ED_AREA_MAGIC_BALL_CONTENT_XPOS, ED_AREA_MAGIC_BALL_CONTENT_YPOS, ED_AREA_MAGIC_BALL_CONTENT_XOFF(1), ED_AREA_MAGIC_BALL_CONTENT_YOFF(1), GADGET_ID_MAGIC_BALL_CONTENT_1, GADGET_ID_NONE, &level.ball_content[1].e[0][0], 3, 3, - NULL, NULL, "2", NULL + NULL, NULL, NULL, "2", NULL }, { ED_AREA_MAGIC_BALL_CONTENT_XPOS, ED_AREA_MAGIC_BALL_CONTENT_YPOS, ED_AREA_MAGIC_BALL_CONTENT_XOFF(2), ED_AREA_MAGIC_BALL_CONTENT_YOFF(2), GADGET_ID_MAGIC_BALL_CONTENT_2, GADGET_ID_NONE, &level.ball_content[2].e[0][0], 3, 3, - NULL, NULL, "3", NULL + NULL, NULL, NULL, "3", NULL }, { ED_AREA_MAGIC_BALL_CONTENT_XPOS, ED_AREA_MAGIC_BALL_CONTENT_YPOS, ED_AREA_MAGIC_BALL_CONTENT_XOFF(3), ED_AREA_MAGIC_BALL_CONTENT_YOFF(3), GADGET_ID_MAGIC_BALL_CONTENT_3, GADGET_ID_NONE, &level.ball_content[3].e[0][0], 3, 3, - NULL, NULL, "4", NULL + NULL, NULL, NULL, "4", NULL }, { ED_AREA_MAGIC_BALL_CONTENT_XPOS, ED_AREA_MAGIC_BALL_CONTENT_YPOS, ED_AREA_MAGIC_BALL_CONTENT_XOFF(4), ED_AREA_MAGIC_BALL_CONTENT_YOFF(4), GADGET_ID_MAGIC_BALL_CONTENT_4, GADGET_ID_NONE, &level.ball_content[4].e[0][0], 3, 3, - NULL, NULL, "5", NULL + NULL, NULL, NULL, "5", NULL }, { ED_AREA_MAGIC_BALL_CONTENT_XPOS, ED_AREA_MAGIC_BALL_CONTENT_YPOS, ED_AREA_MAGIC_BALL_CONTENT_XOFF(5), ED_AREA_MAGIC_BALL_CONTENT_YOFF(5), GADGET_ID_MAGIC_BALL_CONTENT_5, GADGET_ID_NONE, &level.ball_content[5].e[0][0], 3, 3, - NULL, NULL, "6", NULL + NULL, NULL, NULL, "6", NULL }, { ED_AREA_MAGIC_BALL_CONTENT_XPOS, ED_AREA_MAGIC_BALL_CONTENT_YPOS, ED_AREA_MAGIC_BALL_CONTENT_XOFF(6), ED_AREA_MAGIC_BALL_CONTENT_YOFF(6), GADGET_ID_MAGIC_BALL_CONTENT_6, GADGET_ID_NONE, &level.ball_content[6].e[0][0], 3, 3, - NULL, NULL, "7", NULL + NULL, NULL, NULL, "7", NULL }, { ED_AREA_MAGIC_BALL_CONTENT_XPOS, ED_AREA_MAGIC_BALL_CONTENT_YPOS, ED_AREA_MAGIC_BALL_CONTENT_XOFF(7), ED_AREA_MAGIC_BALL_CONTENT_YOFF(7), GADGET_ID_MAGIC_BALL_CONTENT_7, GADGET_ID_NONE, &level.ball_content[7].e[0][0], 3, 3, - NULL, NULL, "8", NULL + NULL, NULL, NULL, "8", NULL }, // ---------- android content ----------------------------------------------- { - ED_AREA_1X1_SETTINGS_XPOS(0), ED_AREA_1X1_SETTINGS_YPOS(5), + ED_AREA_1X1_SETTINGS_XPOS(0), ED_AREA_1X1_SETTINGS_YPOS(6), ED_AREA_1X1_SETTINGS_XOFF, ED_AREA_1X1_SETTINGS_YOFF, GADGET_ID_ANDROID_CONTENT, GADGET_ID_NONE, &level.android_clone_element[0], MAX_ANDROID_ELEMENTS, 1, - "elements:", NULL, NULL, "elements android can clone" + NULL, NULL, "elements:", NULL, "elements android can clone" }, // ---------- amoeba content ------------------------------------------------ @@ -3591,7 +3621,7 @@ static struct ED_AREA_1X1_SETTINGS_XOFF, ED_AREA_1X1_SETTINGS_YOFF, GADGET_ID_AMOEBA_CONTENT, GADGET_ID_NONE, &level.amoeba_content, 1, 1, - "content:", NULL, NULL, "amoeba content" + "content:", NULL, NULL, NULL, "amoeba content" }, // ---------- level start element ------------------------------------------- @@ -3601,7 +3631,7 @@ static struct 0, ED_AREA_1X1_SETTINGS_YOFF, GADGET_ID_START_ELEMENT, GADGET_ID_USE_START_ELEMENT, &level.start_element[0], 1, 1, - NULL, NULL, NULL, "level start element" + NULL, NULL, NULL, NULL, "level start element" }, // ---------- player artwork element ---------------------------------------- @@ -3611,7 +3641,7 @@ static struct 0, ED_AREA_1X1_SETTINGS_YOFF, GADGET_ID_ARTWORK_ELEMENT, GADGET_ID_USE_ARTWORK_ELEMENT, &level.artwork_element[0], 1, 1, - NULL, NULL, NULL, "element for player artwork" + NULL, NULL, NULL, NULL, "element for player artwork" }, // ---------- player explosion element -------------------------------------- @@ -3621,7 +3651,7 @@ static struct 0, ED_AREA_1X1_SETTINGS_YOFF, GADGET_ID_EXPLOSION_ELEMENT, GADGET_ID_USE_EXPLOSION_ELEMENT, &level.explosion_element[0], 1, 1, - NULL, NULL, NULL, "element for player explosion" + NULL, NULL, NULL, NULL, "element for player explosion" }, // ---------- player initial inventory -------------------------------------- @@ -3631,7 +3661,7 @@ static struct 0, ED_AREA_1X1_SETTINGS_YOFF, GADGET_ID_INVENTORY_CONTENT, GADGET_ID_USE_INITIAL_INVENTORY, &level.initial_inventory_content[0][0], MAX_INITIAL_INVENTORY_SIZE, 1, - NULL, NULL, NULL, "content for initial inventory" + NULL, NULL, NULL, NULL, "content for initial inventory" }, // ---------- element settings: configure 1 (custom elements) --------------- @@ -3643,7 +3673,7 @@ static struct 0, ED_AREA_1X1_SETTINGS_YOFF, GADGET_ID_CUSTOM_GRAPHIC, GADGET_ID_CUSTOM_USE_GRAPHIC, &custom_element.gfx_element_initial,1, 1, - NULL, NULL, NULL, "custom graphic element" + NULL, NULL, NULL, NULL, "custom graphic element" }, // ---------- element settings: configure 2 (custom elements) --------------- @@ -3655,7 +3685,7 @@ static struct 0, ED_AREA_3X3_SETTINGS_YOFF, GADGET_ID_CUSTOM_CONTENT, GADGET_ID_NONE, // align three rows &custom_element.content.e[0][0], 3, 3, - "content:", NULL, NULL, NULL + "content:", NULL, NULL, NULL, NULL }, // ---------- custom enter and leave element (when moving) ------------------ @@ -3665,14 +3695,14 @@ static struct ED_AREA_1X1_SETTINGS_XOFF, ED_AREA_1X1_SETTINGS_YOFF, GADGET_ID_CUSTOM_MOVE_ENTER, GADGET_ID_NONE, &custom_element.move_enter_element, 1, 1, - "can dig:", " ", NULL, "element that can be digged/collected" + "can dig:", " ", NULL, NULL, "element that can be digged/collected" }, { -1, ED_AREA_1X1_SETTINGS_YPOS(3), 0, ED_AREA_1X1_SETTINGS_YOFF, GADGET_ID_CUSTOM_MOVE_LEAVE, GADGET_ID_CUSTOM_MOVE_LEAVE_TYPE, &custom_element.move_leave_element, 1, 1, - NULL, NULL, NULL, "element that will be left behind" + NULL, NULL, NULL, NULL, "element that will be left behind" }, // ---------- element settings: advanced (custom elements) ------------------ @@ -3684,7 +3714,7 @@ static struct 0, ED_AREA_1X1_SETTINGS_YOFF, GADGET_ID_CUSTOM_CHANGE_TARGET, GADGET_ID_CUSTOM_CAN_CHANGE, &custom_element_change.target_element, 1, 1, - NULL, "after/when:", NULL, "new target element after change" + NULL, "after/when:", NULL, NULL, "new target element after change" }, // ---------- custom change content (extended change target) ---------------- @@ -3694,7 +3724,7 @@ static struct 0, ED_AREA_3X3_SETTINGS_YOFF, GADGET_ID_CUSTOM_CHANGE_CONTENT, GADGET_ID_NONE, // align three rows &custom_element_change.target_content.e[0][0], 3, 3, - NULL, NULL, NULL, "new extended elements after change" + NULL, NULL, NULL, NULL, "new extended elements after change" }, // ---------- custom change trigger (element causing change) ---------------- @@ -3704,7 +3734,7 @@ static struct 0, ED_AREA_1X1_SETTINGS_YOFF, GADGET_ID_CUSTOM_CHANGE_TRIGGER, GADGET_ID_CHANGE_OTHER_ACTION, &custom_element_change.initial_trigger_element, 1, 1, - NULL, NULL, NULL, "other element triggering change" + NULL, NULL, NULL, NULL, "other element triggering change" }, // ---------- custom change action (element used for action) ---------------- @@ -3714,7 +3744,7 @@ static struct 0, ED_AREA_1X1_SETTINGS_YOFF, GADGET_ID_CUSTOM_CHANGE_ACTION, GADGET_ID_ACTION_ARG, &custom_element_change.action_element, 1, 1, - NULL, NULL, NULL, "element used as action parameter" + NULL, NULL, NULL, NULL, "element used as action parameter" }, // ---------- group element content ----------------------------------------- @@ -3724,7 +3754,7 @@ static struct ED_AREA_1X1_SETTINGS_XOFF, ED_AREA_1X1_SETTINGS_YOFF, GADGET_ID_GROUP_CONTENT, GADGET_ID_NONE, &group_element_info.element[0], MAX_ELEMENTS_IN_GROUP, 1, - "content:", NULL, NULL, NULL + "content:", NULL, NULL, NULL, NULL }, // ---------- random background (for random painting) ----------------------- @@ -3734,7 +3764,7 @@ static struct 0, ED_AREA_1X1_LSETTINGS_YOFF, GADGET_ID_RANDOM_BACKGROUND, GADGET_ID_RANDOM_RESTRICTED, &random_placement_background_element, 1, 1, - NULL, NULL, NULL, "random placement background" + NULL, NULL, NULL, NULL, "random placement background" }, }; @@ -3778,6 +3808,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); @@ -5738,7 +5770,7 @@ static void ReinitializeElementList(void) { // 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++) { @@ -5755,8 +5787,8 @@ static void ReinitializeElementList(void) // 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; } @@ -5788,7 +5820,7 @@ static void ReinitializeElementList(void) { // 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++) { @@ -5810,8 +5842,8 @@ static void ReinitializeElementList(void) // 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++) @@ -7126,7 +7158,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; @@ -7268,8 +7303,10 @@ static void MapDrawingArea(int id) int xoffset_below = getTextWidth(drawingarea_info[id].text_below, font_nr); int x_left = gi->x - xoffset_left; int x_right = gi->x + gi->width + ED_DRAWINGAREA_TEXT_DISTANCE; + int x_above = gi->x - ED_DRAWINGAREA_BORDER_SIZE; int x_below = gi->x + (gi->width - xoffset_below) / 2; int y_side = gi->y + (gi->height - font_height) / 2; + int y_above = gi->y - font_height - ED_DRAWINGAREA_TEXT_DISTANCE; int y_below = gi->y + gi->height + ED_DRAWINGAREA_TEXT_DISTANCE; if (drawingarea_info[id].text_left) @@ -7278,6 +7315,9 @@ static void MapDrawingArea(int id) if (drawingarea_info[id].text_right) DrawText(x_right, y_side, drawingarea_info[id].text_right, font_nr); + if (drawingarea_info[id].text_above) + DrawText(x_above, y_above, drawingarea_info[id].text_above, font_nr); + if (drawingarea_info[id].text_below) DrawText(x_below, y_below, drawingarea_info[id].text_below, font_nr); @@ -7690,6 +7730,17 @@ static boolean PrepareSavingIntoPersonalLevelSet(void) leveldir_current = getTreeInfoFromIdentifier(leveldir_first, getLoginName()); + // this may happen if "setup.internal.create_user_levelset" is FALSE + // or if file "levelinfo.conf" is missing in personal user level set + if (leveldir_current == NULL) + { + Request("Cannot find personal level set?!", REQ_CONFIRM); + + leveldir_current = leveldir_former; + + return FALSE; + } + // find unused level number for (new_level_nr = leveldir_current->first_level; ; new_level_nr++) { @@ -8103,6 +8154,8 @@ static void CopyCustomElementPropertiesToEditor(int element) HAS_CHANGE_EVENT(element, CE_SCORE_CHANGES) ? CE_SCORE_CHANGES : HAS_CHANGE_EVENT(element, CE_VALUE_GETS_ZERO) ? CE_VALUE_GETS_ZERO : HAS_CHANGE_EVENT(element, CE_SCORE_GETS_ZERO) ? CE_SCORE_GETS_ZERO : + HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ? CE_CLICKED_BY_MOUSE : + HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ? CE_PRESSED_BY_MOUSE : custom_element_change.direct_action); // set "change by other element action" selectbox help value @@ -8130,6 +8183,8 @@ static void CopyCustomElementPropertiesToEditor(int element) HAS_CHANGE_EVENT(element, CE_SCORE_CHANGES_OF_X) ? CE_SCORE_CHANGES_OF_X : HAS_CHANGE_EVENT(element, CE_VALUE_GETS_ZERO_OF_X) ? CE_VALUE_GETS_ZERO_OF_X : HAS_CHANGE_EVENT(element, CE_SCORE_GETS_ZERO_OF_X) ? CE_SCORE_GETS_ZERO_OF_X : + HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ? CE_MOUSE_CLICKED_ON_X : + HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X) ? CE_MOUSE_PRESSED_ON_X : custom_element_change.other_action); } @@ -8261,6 +8316,8 @@ static void CopyCustomElementPropertiesToGame(int element) custom_element_change_events[CE_SCORE_CHANGES] = FALSE; custom_element_change_events[CE_VALUE_GETS_ZERO] = FALSE; custom_element_change_events[CE_SCORE_GETS_ZERO] = FALSE; + custom_element_change_events[CE_CLICKED_BY_MOUSE] = FALSE; + custom_element_change_events[CE_PRESSED_BY_MOUSE] = FALSE; custom_element_change_events[custom_element_change.direct_action] = custom_element_change_events[CE_BY_DIRECT_ACTION]; @@ -8288,6 +8345,8 @@ static void CopyCustomElementPropertiesToGame(int element) custom_element_change_events[CE_SCORE_CHANGES_OF_X] = FALSE; custom_element_change_events[CE_VALUE_GETS_ZERO_OF_X] = FALSE; custom_element_change_events[CE_SCORE_GETS_ZERO_OF_X] = FALSE; + custom_element_change_events[CE_MOUSE_CLICKED_ON_X] = FALSE; + custom_element_change_events[CE_MOUSE_PRESSED_ON_X] = FALSE; custom_element_change_events[custom_element_change.other_action] = custom_element_change_events[CE_BY_OTHER_ACTION]; @@ -8300,6 +8359,9 @@ static void CopyCustomElementPropertiesToGame(int element) // copy change events also to special level editor variable custom_element = element_info[element]; custom_element_change = *element_info[element].change; + + // needed here to restore runtime value "element_info[element].gfx_element" + InitElementPropertiesGfxElement(); } static void CopyGroupElementPropertiesToGame(int element) @@ -8313,6 +8375,9 @@ static void CopyGroupElementPropertiesToGame(int element) element_info[element] = custom_element; *element_info[element].group = group_element_info; + + // needed here to restore runtime value "element_info[element].gfx_element" + InitElementPropertiesGfxElement(); } static void CopyClassicElementPropertiesToGame(int element) @@ -8377,6 +8442,9 @@ static void InitZoomLevelSettings(int zoom_tilesize) ed_tilesize = setup.auto_setup.editor_zoom_tilesize; ed_tilesize_default = DEFAULT_EDITOR_TILESIZE; + // make sure that tile size is always a power of 2 + ed_tilesize = (1 << log_2(ed_tilesize)); + if (level.game_engine_type == GAME_ENGINE_TYPE_MM) { ed_tilesize = DEFAULT_EDITOR_TILESIZE_MM; @@ -8478,29 +8546,31 @@ static boolean useEditorDoorAnimation(void) 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(); @@ -8519,7 +8589,7 @@ void DrawLevelEd(void) FadeSoundsAndMusic(); - if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged()) + if (CheckFadeAll()) fade_mask = REDRAW_ALL; FadeOut(fade_mask); @@ -9725,10 +9795,43 @@ static void SetAutomaticNumberOfGemsNeeded(void) { 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; + } } } @@ -9811,7 +9914,7 @@ static void DrawPropertiesConfig(void) DrawMagicBallContentAreas(); MapCheckbuttonGadget(ED_CHECKBUTTON_ID_RANDOM_BALL_CONTENT); - MapCheckbuttonGadget(ED_CHECKBUTTON_ID_INITIAL_BALL_STATE); + MapCheckbuttonGadget(ED_CHECKBUTTON_ID_INITIAL_BALL_ACTIVE); } else if (properties_element == EL_EMC_ANDROID) DrawAndroidElementArea(properties_element); @@ -9931,10 +10034,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); @@ -10804,7 +10919,6 @@ static void SetElementIntelliDraw(int x, int y, int new_element, }; static int last_x = -1; static int last_y = -1; - int old_element = IntelliDrawBuffer[x][y]; if (new_element == EL_UNDEFINED) { @@ -10814,6 +10928,8 @@ static void SetElementIntelliDraw(int x, int y, int new_element, return; } + int old_element = IntelliDrawBuffer[x][y]; + if (IS_TUBE(new_element)) { int last_element_new = EL_UNDEFINED; @@ -11588,11 +11704,21 @@ static void ResetIntelliDraw(void) 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) @@ -11865,12 +11991,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) { @@ -11883,41 +12020,224 @@ 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) + if (edit_mode != ED_MODE_DRAWING) + return; + + 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); + char *format = "%s%03d"; + + for (y = 0; y < height; y++) + for (x = 0; x < width; x++) + if ((draw_with_brush ? brush_buffer[x][y] : Feld[x][y]) > 999) + format = "%s%04d"; + + for (y = 0; y < height; y++) + { + for (x = 0; x < width; x++) + { + int element = (draw_with_brush ? brush_buffer[x][y] : Feld[x][y]); + char *prefix = (mode == CB_DUMP_BRUSH || + mode == CB_BRUSH_TO_CLIPBOARD ? "`" : "¸"); + + if (element >= NUM_FILE_ELEMENTS) + element = EL_UNKNOWN; + + // copy brush to level sketch text buffer for the R'n'D forum: + // - large tiles: `xxx or `xxxx (0x60 ASCII) + // - small tiles: ¸xxx or ¸xxxx (0xb8 ISO-8859-1, 0xc2b8 UTF-8) + snprintf(part, MAX_CB_PART_SIZE + 1, format, prefix, element); + 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()) { - Error(ERR_WARN, "no brush selected"); + Request("Clipboard is empty!", REQ_CONFIRM); return; } - for (y = 0; y < brush_height; y++) + 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 allow_new_row = FALSE; + boolean stop = FALSE; + + while (*ptr && !stop) { - for (x = 0; x < brush_width; x++) + boolean prefix_found = FALSE; + boolean start_new_row = 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++) { - int element = brush_buffer[x][y]; - int element_mapped = element; - - if (IS_CUSTOM_ELEMENT(element)) - element_mapped = EL_CUSTOM_START; - else if (IS_GROUP_ELEMENT(element)) - element_mapped = EL_GROUP_START; - else if (element >= NUM_FILE_ELEMENTS) - element_mapped = EL_UNKNOWN; - - // dump brush as level sketch text 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); + 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; + } } - printf("\n"); + // check if prefix found and followed by (at least) three digits + if (prefix_found && + strlen(ptr) >= 3 && + 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; + + // level sketch element number might consist of four digits + if (ptr[0] >= '0' && ptr[0] <= '9') + { + element = element * 10 + (ptr[0] - '0'); + ptr++; + } + + 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) + start_new_row = TRUE; + + allow_new_row = TRUE; + } + else + { + if ((*ptr == '\n' || *ptr == '\r') && allow_new_row) + start_new_row = TRUE; + + ptr++; // !!! FIX THIS for real UTF-8 handling !!! + } + + if (start_new_row) + { + x = 0; + y++; + + if (y >= MAX_LEV_FIELDY) + stop = TRUE; + + allow_new_row = FALSE; + } + } + + 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; @@ -11975,6 +12295,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; } @@ -12012,8 +12333,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, @@ -12032,6 +12360,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); @@ -12047,6 +12380,31 @@ 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); +} + +void UndoLevelEditorOperation(void) +{ + ClickOnGadget(level_editor_gadget[GADGET_ID_UNDO], -1); +} + +void RedoLevelEditorOperation(void) +{ + ClickOnGadget(level_editor_gadget[GADGET_ID_UNDO], 3); +} + static void FloodFill(int from_x, int from_y, int fill_element) { FloodFillLevel(from_x, from_y, fill_element, Feld, lev_fieldx, lev_fieldy); @@ -12137,6 +12495,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; @@ -12172,7 +12532,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); @@ -12181,6 +12541,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; @@ -12423,9 +12784,13 @@ static void HandleDrawingAreas(struct GadgetInfo *gi) } 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; } @@ -12495,13 +12860,37 @@ static void HandleDrawingAreas(struct GadgetInfo *gi) { SetDrawModeHiRes(new_element); - if (new_element == EL_PLAYER_1) + if (ELEM_IS_PLAYER(new_element)) { // remove player at old position for (y = 0; y < lev_fieldy; y++) + { for (x = 0; x < lev_fieldx; x++) - if (Feld[x][y] == EL_PLAYER_1) - SetElement(x, y, EL_EMPTY); + { + int old_element = Feld[x][y]; + + if (ELEM_IS_PLAYER(old_element)) + { + int replaced_with_element = + (old_element == EL_SOKOBAN_FIELD_PLAYER && + new_element == EL_PLAYER_1 ? EL_SOKOBAN_FIELD_EMPTY : + + old_element == EL_SOKOBAN_FIELD_PLAYER && + new_element == old_element ? EL_SOKOBAN_FIELD_EMPTY : + + new_element == EL_SOKOBAN_FIELD_PLAYER && + old_element == EL_PLAYER_1 ? EL_EMPTY : + + new_element >= EL_PLAYER_1 && + new_element <= EL_PLAYER_4 && + new_element == old_element ? EL_EMPTY : + + old_element); + + SetElement(x, y, replaced_with_element); + } + } + } } SetElementButton(lx, ly, dx, dy, new_element, button); @@ -13443,6 +13832,10 @@ static void HandleControlButtons(struct GadgetInfo *gi) button == 2 ? ed_tilesize_default : button == 3 ? ed_tilesize / 2 : ed_tilesize); + // when using touch device, cycle through all zoom tilesizes + if (runtime.uses_touch_device && ed_tilesize > TILESIZE) + ed_tilesize = MICRO_TILESIZE; + // limit zoom level by upper and lower bound ed_tilesize = MIN(MAX(MICRO_TILESIZE, ed_tilesize), TILESIZE); @@ -13477,7 +13870,9 @@ static void HandleControlButtons(struct GadgetInfo *gi) break; case GADGET_ID_UNDO: - if (button == 1 && GetKeyModState() & (KMOD_Shift|KMOD_Control)) + if (button < 0) // keep button value (even if modifier keys are pressed) + button = -button; + else if (button == 1 && GetKeyModState() & (KMOD_Shift | KMOD_Control)) button = 3; if (button == 1 && undo_buffer_steps == 0) @@ -13728,7 +14123,6 @@ static void HandleControlButtons(struct GadgetInfo *gi) void HandleLevelEditorKeyInput(Key key) { char letter = getCharFromKey(key); - int button = MB_LEFTBUTTON; if (drawing_function == GADGET_ID_TEXT && DrawLevelText(0, 0, 0, TEXT_QUERY_TYPING) == TRUE) @@ -13741,150 +14135,141 @@ void HandleLevelEditorKeyInput(Key key) DrawLevelText(0, 0, 0, TEXT_NEWLINE); else if (key == KSYM_Escape) DrawLevelText(0, 0, 0, TEXT_END); + + return; } - else if (button_status == MB_RELEASED) - { - int id = GADGET_ID_NONE; - int new_element_shift = element_shift; - int step = ED_ELEMENTLIST_BUTTONS_VERT - 1; - int i; - switch (key) - { - case KSYM_Left: - id = GADGET_ID_SCROLL_LEFT; - break; - case KSYM_Right: - id = GADGET_ID_SCROLL_RIGHT; - break; - case KSYM_Up: - id = GADGET_ID_SCROLL_UP; - break; - case KSYM_Down: - id = GADGET_ID_SCROLL_DOWN; - break; + int id = GADGET_ID_NONE; + int new_element_shift = element_shift; + int step = ED_ELEMENTLIST_BUTTONS_VERT - 1; + int button = MB_LEFTBUTTON; + int i; - case KSYM_Page_Up: - case KSYM_Page_Down: - step *= (key == KSYM_Page_Up ? -1 : +1); - element_shift += step * ED_ELEMENTLIST_BUTTONS_HORIZ; + switch (key) + { + case KSYM_Left: + id = GADGET_ID_SCROLL_LEFT; + break; + case KSYM_Right: + id = GADGET_ID_SCROLL_RIGHT; + break; + case KSYM_Up: + id = GADGET_ID_SCROLL_UP; + break; + case KSYM_Down: + id = GADGET_ID_SCROLL_DOWN; + break; - if (element_shift < 0) - element_shift = 0; - if (element_shift > num_editor_elements - ED_NUM_ELEMENTLIST_BUTTONS) - element_shift = num_editor_elements - ED_NUM_ELEMENTLIST_BUTTONS; + case KSYM_Page_Up: + case KSYM_Page_Down: + step *= (key == KSYM_Page_Up ? -1 : +1); + element_shift += step * ED_ELEMENTLIST_BUTTONS_HORIZ; - ModifyGadget(level_editor_gadget[GADGET_ID_SCROLL_LIST_VERTICAL], - GDI_SCROLLBAR_ITEM_POSITION, - element_shift / ED_ELEMENTLIST_BUTTONS_HORIZ, GDI_END); + if (element_shift < 0) + element_shift = 0; + if (element_shift > num_editor_elements - ED_NUM_ELEMENTLIST_BUTTONS) + element_shift = num_editor_elements - ED_NUM_ELEMENTLIST_BUTTONS; - ModifyEditorElementList(); + ModifyGadget(level_editor_gadget[GADGET_ID_SCROLL_LIST_VERTICAL], + GDI_SCROLLBAR_ITEM_POSITION, + element_shift / ED_ELEMENTLIST_BUTTONS_HORIZ, GDI_END); - break; + ModifyEditorElementList(); - case KSYM_Home: - case KSYM_End: - element_shift = (key == KSYM_Home ? 0 : - num_editor_elements - ED_NUM_ELEMENTLIST_BUTTONS); + break; - ModifyGadget(level_editor_gadget[GADGET_ID_SCROLL_LIST_VERTICAL], - GDI_SCROLLBAR_ITEM_POSITION, - element_shift / ED_ELEMENTLIST_BUTTONS_HORIZ, GDI_END); + case KSYM_Home: + case KSYM_End: + element_shift = (key == KSYM_Home ? 0 : + num_editor_elements - ED_NUM_ELEMENTLIST_BUTTONS); - ModifyEditorElementList(); + ModifyGadget(level_editor_gadget[GADGET_ID_SCROLL_LIST_VERTICAL], + GDI_SCROLLBAR_ITEM_POSITION, + element_shift / ED_ELEMENTLIST_BUTTONS_HORIZ, GDI_END); - break; + ModifyEditorElementList(); - case KSYM_Insert: - case KSYM_Delete: + break; - // this is needed to prevent interference with running "True X-Mouse" - if (GetKeyModStateFromEvents() & KMOD_Control) - break; + case KSYM_Insert: + case KSYM_Delete: - // check for last or next editor cascade block in element list - for (i = 0; i < num_editor_elements; i++) - { - if ((key == KSYM_Insert && i == element_shift) || - (key == KSYM_Delete && new_element_shift > element_shift)) - break; + // this is needed to prevent interference with running "True X-Mouse" + if (GetKeyModStateFromEvents() & KMOD_Control) + break; - // jump to next cascade block (or to start of element list) - if (i == 0 || IS_EDITOR_CASCADE(editor_elements[i])) - new_element_shift = i; - } + // check for last or next editor cascade block in element list + for (i = 0; i < num_editor_elements; i++) + { + if ((key == KSYM_Insert && i == element_shift) || + (key == KSYM_Delete && new_element_shift > element_shift)) + break; - if (i < num_editor_elements) - element_shift = new_element_shift; + // jump to next cascade block (or to start of element list) + if (i == 0 || IS_EDITOR_CASCADE(editor_elements[i])) + new_element_shift = i; + } - if (element_shift > num_editor_elements - ED_NUM_ELEMENTLIST_BUTTONS) - element_shift = num_editor_elements - ED_NUM_ELEMENTLIST_BUTTONS; + if (i < num_editor_elements) + element_shift = new_element_shift; - ModifyGadget(level_editor_gadget[GADGET_ID_SCROLL_LIST_VERTICAL], - GDI_SCROLLBAR_ITEM_POSITION, - element_shift / ED_ELEMENTLIST_BUTTONS_HORIZ, GDI_END); + if (element_shift > num_editor_elements - ED_NUM_ELEMENTLIST_BUTTONS) + element_shift = num_editor_elements - ED_NUM_ELEMENTLIST_BUTTONS; - ModifyEditorElementList(); + ModifyGadget(level_editor_gadget[GADGET_ID_SCROLL_LIST_VERTICAL], + GDI_SCROLLBAR_ITEM_POSITION, + element_shift / ED_ELEMENTLIST_BUTTONS_HORIZ, GDI_END); - break; + ModifyEditorElementList(); - case KSYM_Escape: - if (edit_mode == ED_MODE_DRAWING) - { - RequestExitLevelEditor(setup.ask_on_escape_editor, TRUE); - } - else if (edit_mode == ED_MODE_INFO) - { - HandleControlButtons(level_editor_gadget[GADGET_ID_INFO]); - } - else if (edit_mode == ED_MODE_PROPERTIES) - { - HandleControlButtons(level_editor_gadget[GADGET_ID_PROPERTIES]); - } - else if (edit_mode == ED_MODE_PALETTE) - { - HandleControlButtons(level_editor_gadget[GADGET_ID_PALETTE]); - } - else // should never happen - { - ChangeEditModeWindow(ED_MODE_DRAWING); - } + break; - break; + case KSYM_Escape: + if (edit_mode == ED_MODE_DRAWING) + RequestExitLevelEditor(setup.ask_on_escape_editor, TRUE); + else if (edit_mode == ED_MODE_INFO) + HandleControlButtons(level_editor_gadget[GADGET_ID_INFO]); + else if (edit_mode == ED_MODE_PROPERTIES) + HandleControlButtons(level_editor_gadget[GADGET_ID_PROPERTIES]); + else if (edit_mode == ED_MODE_PALETTE) + HandleControlButtons(level_editor_gadget[GADGET_ID_PALETTE]); + else // should never happen + ChangeEditModeWindow(ED_MODE_DRAWING); - default: - break; - } + break; - if (id != GADGET_ID_NONE) - ClickOnGadget(level_editor_gadget[id], button); - else if (letter == '1' || letter == '?') - ClickOnGadget(level_editor_gadget[GADGET_ID_ELEMENT_LEFT], button); - else if (letter == '2') - ClickOnGadget(level_editor_gadget[GADGET_ID_ELEMENT_MIDDLE], button); - else if (letter == '3') - ClickOnGadget(level_editor_gadget[GADGET_ID_ELEMENT_RIGHT], button); - else if (letter == '.') - ClickOnGadget(level_editor_gadget[GADGET_ID_SINGLE_ITEMS], button); - else if (letter == 'U') - ClickOnGadget(level_editor_gadget[GADGET_ID_UNDO], 3); - else if (letter == '-' || key == KSYM_KP_Subtract) - ClickOnGadget(level_editor_gadget[GADGET_ID_ZOOM], 3); - else if (letter == '0' || key == KSYM_KP_0) - ClickOnGadget(level_editor_gadget[GADGET_ID_ZOOM], 2); - else if (letter == '+' || key == KSYM_KP_Add || - letter == '=') // ("Shift-=" is "+" on US keyboards) - ClickOnGadget(level_editor_gadget[GADGET_ID_ZOOM], 1); - else if (key == KSYM_Return || - key == KSYM_space || - key == setup.shortcut.toggle_pause) - ClickOnGadget(level_editor_gadget[GADGET_ID_TEST], button); - else - for (i = 0; i < ED_NUM_CTRL_BUTTONS; i++) - if (letter && letter == controlbutton_info[i].shortcut) - if (!anyTextGadgetActive()) - ClickOnGadget(level_editor_gadget[i], button); + default: + break; } + + if (id != GADGET_ID_NONE) + ClickOnGadget(level_editor_gadget[id], button); + else if (letter == '1' || letter == '?') + ClickOnGadget(level_editor_gadget[GADGET_ID_ELEMENT_LEFT], button); + else if (letter == '2') + ClickOnGadget(level_editor_gadget[GADGET_ID_ELEMENT_MIDDLE], button); + else if (letter == '3') + ClickOnGadget(level_editor_gadget[GADGET_ID_ELEMENT_RIGHT], button); + else if (letter == '.') + ClickOnGadget(level_editor_gadget[GADGET_ID_SINGLE_ITEMS], button); + else if (letter == 'U') + ClickOnGadget(level_editor_gadget[GADGET_ID_UNDO], 3); + else if (letter == '-' || key == KSYM_KP_Subtract) + ClickOnGadget(level_editor_gadget[GADGET_ID_ZOOM], 3); + else if (letter == '0' || key == KSYM_KP_0) + ClickOnGadget(level_editor_gadget[GADGET_ID_ZOOM], 2); + else if (letter == '+' || key == KSYM_KP_Add || + letter == '=') // ("Shift-=" is "+" on US keyboards) + ClickOnGadget(level_editor_gadget[GADGET_ID_ZOOM], 1); + else if (key == KSYM_Return || + key == KSYM_space || + key == setup.shortcut.toggle_pause) + ClickOnGadget(level_editor_gadget[GADGET_ID_TEST], button); + else + for (i = 0; i < ED_NUM_CTRL_BUTTONS; i++) + if (letter && letter == controlbutton_info[i].shortcut) + if (!anyTextGadgetActive()) + ClickOnGadget(level_editor_gadget[i], button); } void HandleLevelEditorIdle(void) @@ -14101,6 +14486,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) {