Merge branch 'master' into releases 3.0.3
authorHolger Schemel <info@artsoft.org>
Sat, 30 Aug 2014 08:43:44 +0000 (10:43 +0200)
committerHolger Schemel <info@artsoft.org>
Sat, 30 Aug 2014 08:43:44 +0000 (10:43 +0200)
26 files changed:
CHANGES
src/cartoons.c
src/conf_fnt.c
src/conf_gfx.c
src/conf_gfx.h
src/conftime.h
src/editor.c
src/files.c
src/files.h
src/game.c
src/init.c
src/libgame/gadgets.c
src/libgame/gadgets.h
src/libgame/misc.c
src/libgame/sdl.c
src/libgame/setup.c
src/libgame/sound.c
src/libgame/system.h
src/libgame/text.c
src/libgame/text.h
src/libgame/toons.c
src/main.c
src/main.h
src/screens.c
src/tools.c
src/tools.h

diff --git a/CHANGES b/CHANGES
index 347e1d7d16faec704bd70b536134da21d1e10acb..6f704695e25a08524f35fc008135b791b539f084 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,14 @@
+Release Version 3.0.3 [08 SEP 2003]
+-----------------------------------
+       - fixed bug (missing array boundary check) which could crash the game
+       - fixed problem with setting mouse cursor in SDL version in fullscreen
+       - fixed problem with flickering when drawing toon animations
+       - fixed problem with player animation when snapping and moving
+       - fixed several nasty bugs that may have caused crashes on some systems
+       - added envelope content which gets displayed when collecting envelope
+       - added multiple change event pages for custom elements
+       - added support for loading various music formats through SDL_mixer
+
 Release Version 3.0.2 [22 AUG 2003]
 -----------------------------------
        - fixed bug with messing up custom element properties in 3.0.0 levels
index 5bacdf29c3aad8c6dcd3d1e6c3984617d3a92a87..e6892ebb3c28a2addeba225d12ca75066b82f623 100644 (file)
@@ -51,10 +51,16 @@ static void PrepareBackbuffer()
 
 boolean ToonNeedsRedraw()
 {
+#if 1
+  return TRUE;
+#else
   return (game_status == GAME_MODE_INFO ||
+         game_status == GAME_MODE_LEVELS ||
+         game_status == GAME_MODE_SETUP ||
          (game_status == GAME_MODE_MAIN &&
           ((redraw_mask & REDRAW_MICROLEVEL) ||
            (redraw_mask & REDRAW_MICROLABEL))));
+#endif
 }
 
 void InitToons()
index 0a191dda15f257a76f19a1f25fb52e882c988d34..ef44d5383c5bc8d4c9bf4611ce53264338907131 100644 (file)
@@ -75,6 +75,10 @@ font_to_graphic[] =
     FONT_TEXT_1,                               GFX_SPECIAL_ARG_PREVIEW,
     IMG_FONT_TEXT_1_PREVIEW
   },
+  {
+    FONT_TEXT_1,                               GFX_SPECIAL_ARG_PLAYING,
+    IMG_FONT_TEXT_1_PLAYING
+  },
   {
     FONT_TEXT_1,                               GFX_SPECIAL_ARG_SCORES,
     IMG_FONT_TEXT_1_SCORES
index 258b7fde0750671b85b9d325a81940bade85311d..55c6f3875c09d3e1fe7c57e2363bcfeea49027a4 100644 (file)
@@ -39,12 +39,14 @@ struct ConfigInfo image_config_suffix[] =
   { ".global_sync",                    "false",        TYPE_BOOLEAN },
   { ".crumbled_like",                  ARG_UNDEFINED,  TYPE_TOKEN   },
   { ".diggable_like",                  ARG_UNDEFINED,  TYPE_TOKEN   },
+  { ".border_size",                    ARG_UNDEFINED,  TYPE_INTEGER },
   { ".step_offset",                    "4",            TYPE_INTEGER },
   { ".step_delay",                     "1",            TYPE_INTEGER },
   { ".direction",                      ARG_UNDEFINED,  TYPE_STRING  },
   { ".position",                       ARG_UNDEFINED,  TYPE_STRING  },
   { ".draw_xoffset",                   "0",            TYPE_INTEGER },
   { ".draw_yoffset",                   "0",            TYPE_INTEGER },
+  { ".draw_masked",                    "false",        TYPE_BOOLEAN },
   { ".name",                           ARG_UNDEFINED,  TYPE_STRING  },
 
   { NULL,                              NULL,           0            }
@@ -3263,6 +3265,11 @@ struct ConfigInfo image_config[] =
   { "font.text_1.PREVIEW.y",                   "160"                   },
   { "font.text_1.PREVIEW.width",               "16"                    },
   { "font.text_1.PREVIEW.height",              "16"                    },
+  { "font.text_1.PLAYING",                     "RocksFontEM.pcx"       },
+  { "font.text_1.PLAYING.x",                   "0"                     },
+  { "font.text_1.PLAYING.y",                   "160"                   },
+  { "font.text_1.PLAYING.width",               "16"                    },
+  { "font.text_1.PLAYING.height",              "16"                    },
   { "font.text_1.SCORES",                      "RocksFontMedium.pcx"   },
   { "font.text_1.SCORES.x",                    "0"                     },
   { "font.text_1.SCORES.y",                    "480"                   },
@@ -3435,6 +3442,12 @@ struct ConfigInfo image_config[] =
   { "editor.element_border_input.xpos",                "0"                     },
   { "editor.element_border_input.ypos",                "0"                     },
 
+  { "game.envelope_background",                        "RocksScreen.pcx"       },
+  { "game.envelope_background.x",              "0"                     },
+  { "game.envelope_background.y",              "0"                     },
+  { "game.envelope_background.width",          "560"                   },
+  { "game.envelope_background.height",         "560"                   },
+
   { "background",                              UNDEFINED_FILENAME      },
   { "background.MAIN",                         UNDEFINED_FILENAME      },
   { "background.LEVELS",                       UNDEFINED_FILENAME      },
index 41fe2b22700ff03e067fa8cc47790449c846d233..9033fc69790a0947b4508351a4b118c3ed578dc5 100644 (file)
 #define IMG_FONT_TEXT_1                                        1227
 #define IMG_FONT_TEXT_1_LEVELS                         1228
 #define IMG_FONT_TEXT_1_PREVIEW                                1229
-#define IMG_FONT_TEXT_1_SCORES                         1230
-#define IMG_FONT_TEXT_1_ACTIVE_SCORES                  1231
-#define IMG_FONT_TEXT_2                                        1232
-#define IMG_FONT_TEXT_2_LEVELS                         1233
-#define IMG_FONT_TEXT_2_PREVIEW                                1234
-#define IMG_FONT_TEXT_2_SCORES                         1235
-#define IMG_FONT_TEXT_2_ACTIVE_SCORES                  1236
-#define IMG_FONT_TEXT_3                                        1237
-#define IMG_FONT_TEXT_3_LEVELS                         1238
-#define IMG_FONT_TEXT_3_PREVIEW                                1239
-#define IMG_FONT_TEXT_3_SCORES                         1240
-#define IMG_FONT_TEXT_3_ACTIVE_SCORES                  1241
-#define IMG_FONT_TEXT_4                                        1242
-#define IMG_FONT_TEXT_4_LEVELS                         1243
-#define IMG_FONT_TEXT_4_SCORES                         1244
-#define IMG_FONT_TEXT_4_ACTIVE_SCORES                  1245
-#define IMG_FONT_INPUT_1                               1246
-#define IMG_FONT_INPUT_1_MAIN                          1247
-#define IMG_FONT_INPUT_1_ACTIVE                                1248
-#define IMG_FONT_INPUT_1_ACTIVE_MAIN                   1249
-#define IMG_FONT_INPUT_1_ACTIVE_SETUP                  1250
-#define IMG_FONT_INPUT_2                               1251
-#define IMG_FONT_INPUT_2_ACTIVE                                1252
-#define IMG_FONT_OPTION_OFF                            1253
-#define IMG_FONT_OPTION_ON                             1254
-#define IMG_FONT_VALUE_1                               1255
-#define IMG_FONT_VALUE_2                               1256
-#define IMG_FONT_VALUE_OLD                             1257
-#define IMG_FONT_LEVEL_NUMBER                          1258
-#define IMG_FONT_TAPE_RECORDER                         1259
-#define IMG_FONT_GAME_INFO                             1260
-#define IMG_GLOBAL_BORDER                              1261
-#define IMG_GLOBAL_DOOR                                        1262
-#define IMG_EDITOR_ELEMENT_BORDER                      1263
-#define IMG_EDITOR_ELEMENT_BORDER_INPUT                        1264
-#define IMG_BACKGROUND                                 1265
-#define IMG_BACKGROUND_MAIN                            1266
-#define IMG_BACKGROUND_LEVELS                          1267
-#define IMG_BACKGROUND_SCORES                          1268
-#define IMG_BACKGROUND_EDITOR                          1269
-#define IMG_BACKGROUND_INFO                            1270
-#define IMG_BACKGROUND_SETUP                           1271
-#define IMG_BACKGROUND_DOOR                            1272
+#define IMG_FONT_TEXT_1_PLAYING                                1230
+#define IMG_FONT_TEXT_1_SCORES                         1231
+#define IMG_FONT_TEXT_1_ACTIVE_SCORES                  1232
+#define IMG_FONT_TEXT_2                                        1233
+#define IMG_FONT_TEXT_2_LEVELS                         1234
+#define IMG_FONT_TEXT_2_PREVIEW                                1235
+#define IMG_FONT_TEXT_2_SCORES                         1236
+#define IMG_FONT_TEXT_2_ACTIVE_SCORES                  1237
+#define IMG_FONT_TEXT_3                                        1238
+#define IMG_FONT_TEXT_3_LEVELS                         1239
+#define IMG_FONT_TEXT_3_PREVIEW                                1240
+#define IMG_FONT_TEXT_3_SCORES                         1241
+#define IMG_FONT_TEXT_3_ACTIVE_SCORES                  1242
+#define IMG_FONT_TEXT_4                                        1243
+#define IMG_FONT_TEXT_4_LEVELS                         1244
+#define IMG_FONT_TEXT_4_SCORES                         1245
+#define IMG_FONT_TEXT_4_ACTIVE_SCORES                  1246
+#define IMG_FONT_INPUT_1                               1247
+#define IMG_FONT_INPUT_1_MAIN                          1248
+#define IMG_FONT_INPUT_1_ACTIVE                                1249
+#define IMG_FONT_INPUT_1_ACTIVE_MAIN                   1250
+#define IMG_FONT_INPUT_1_ACTIVE_SETUP                  1251
+#define IMG_FONT_INPUT_2                               1252
+#define IMG_FONT_INPUT_2_ACTIVE                                1253
+#define IMG_FONT_OPTION_OFF                            1254
+#define IMG_FONT_OPTION_ON                             1255
+#define IMG_FONT_VALUE_1                               1256
+#define IMG_FONT_VALUE_2                               1257
+#define IMG_FONT_VALUE_OLD                             1258
+#define IMG_FONT_LEVEL_NUMBER                          1259
+#define IMG_FONT_TAPE_RECORDER                         1260
+#define IMG_FONT_GAME_INFO                             1261
+#define IMG_GLOBAL_BORDER                              1262
+#define IMG_GLOBAL_DOOR                                        1263
+#define IMG_EDITOR_ELEMENT_BORDER                      1264
+#define IMG_EDITOR_ELEMENT_BORDER_INPUT                        1265
+#define IMG_GAME_ENVELOPE_BACKGROUND                   1266
+#define IMG_BACKGROUND                                 1267
+#define IMG_BACKGROUND_MAIN                            1268
+#define IMG_BACKGROUND_LEVELS                          1269
+#define IMG_BACKGROUND_SCORES                          1270
+#define IMG_BACKGROUND_EDITOR                          1271
+#define IMG_BACKGROUND_INFO                            1272
+#define IMG_BACKGROUND_SETUP                           1273
+#define IMG_BACKGROUND_DOOR                            1274
 
-#define NUM_IMAGE_FILES                                        1273
+#define NUM_IMAGE_FILES                                        1275
 
 #endif /* CONF_GFX_H */
index 6d2a47a8bc5ff800e6182e5f3bf39f90e561c62e..bd0e81872db861fd33fb13945c91ee975e635dfd 100644 (file)
@@ -1 +1 @@
-#define COMPILE_DATE_STRING "[2003-08-21 23:55]"
+#define COMPILE_DATE_STRING "[2003-09-08 00:04]"
index 4a47194a4066bd6b346616bbca718db1f1a1c7b8..bc79a60621764d53dee7949a6d61e6e1c2086c93 100644 (file)
 #define ED_SETTINGS2_YPOS              (ED_SETTINGS1_YPOS + 12 * TILEY - 2)
 
 /* values for counter gadgets */
-#define ED_COUNT_PUSH_DELAY_RND_XPOS   (ED_SETTINGS_XPOS(1) + 16 * MINI_TILEX)
-#define ED_COUNT_MOVE_DELAY_RND_XPOS   ED_COUNT_PUSH_DELAY_RND_XPOS
-#define ED_COUNT_CHANGE_DELAY_RND_XPOS (ED_SETTINGS_XPOS(1) + 13 * MINI_TILEX)
-
 #define ED_COUNTER_YSTART              (ED_SETTINGS1_YPOS + 2 * TILEY)
 #define ED_COUNTER_YDISTANCE           (3 * MINI_TILEY)
 #define ED_COUNTER_YPOS(n)             (ED_COUNTER_YSTART + \
                                         6 * (n / 4) * MINI_TILEY)
 
 /* custom change target */
-#define ED_AREA_ELEM_CONTENT2_XPOS     (20 * MINI_TILEX)
 #define ED_AREA_ELEM_CONTENT2_YPOS     (ED_SETTINGS_YPOS(2) + \
                                         ED_GADGET_DISTANCE)
 /* optional custom graphic */
-#define ED_AREA_ELEM_CONTENT3_XPOS     (24 * MINI_TILEX)
 #define ED_AREA_ELEM_CONTENT3_YPOS     (ED_SETTINGS_YPOS(1) + \
                                         ED_GADGET_DISTANCE)
 /* custom element content */
 #define ED_AREA_ELEM_CONTENT4_YPOS     (ED_SETTINGS_YPOS(12) + \
                                         ED_GADGET_DISTANCE - MINI_TILEY)
 /* custom change trigger element */
-#define ED_AREA_ELEM_CONTENT5_XPOS     (28 * MINI_TILEX)
-#define ED_AREA_ELEM_CONTENT5_YPOS     (ED_SETTINGS_YPOS(7) + \
+#define ED_AREA_ELEM_CONTENT5_YPOS     (ED_SETTINGS_YPOS(6) + \
                                         ED_GADGET_DISTANCE)
 /* extended custom change target */
 #define ED_AREA_ELEM_CONTENT6_XPOS     (29 * MINI_TILEX)
-#define ED_AREA_ELEM_CONTENT6_YPOS     (ED_SETTINGS_YPOS(10) + \
+#define ED_AREA_ELEM_CONTENT6_YPOS     (ED_SETTINGS_YPOS(9) + \
                                         ED_GADGET_DISTANCE - MINI_TILEY)
 
 /* values for random placement background drawing area */
-#define ED_AREA_RANDOM_BACKGROUND_XPOS (29 * MINI_TILEX)
 #define ED_AREA_RANDOM_BACKGROUND_YPOS (31 * MINI_TILEY)
 
 /* values for scrolling gadgets for drawing area */
 #define GADGET_ID_ELEMENT_CONTENT_DOWN (GADGET_ID_COUNTER_FIRST + 24)
 #define GADGET_ID_ELEMENT_CONTENT_TEXT (GADGET_ID_COUNTER_FIRST + 25)
 #define GADGET_ID_ELEMENT_CONTENT_UP   (GADGET_ID_COUNTER_FIRST + 26)
-#define GADGET_ID_CUSTOM_SCORE_DOWN    (GADGET_ID_COUNTER_FIRST + 27)
-#define GADGET_ID_CUSTOM_SCORE_TEXT    (GADGET_ID_COUNTER_FIRST + 28)
-#define GADGET_ID_CUSTOM_SCORE_UP      (GADGET_ID_COUNTER_FIRST + 29)
-#define GADGET_ID_CUSTOM_GEMCOUNT_DOWN (GADGET_ID_COUNTER_FIRST + 30)
-#define GADGET_ID_CUSTOM_GEMCOUNT_TEXT (GADGET_ID_COUNTER_FIRST + 31)
-#define GADGET_ID_CUSTOM_GEMCOUNT_UP   (GADGET_ID_COUNTER_FIRST + 32)
-#define GADGET_ID_PUSH_DELAY_FIX_DOWN  (GADGET_ID_COUNTER_FIRST + 33)
-#define GADGET_ID_PUSH_DELAY_FIX_TEXT  (GADGET_ID_COUNTER_FIRST + 34)
-#define GADGET_ID_PUSH_DELAY_FIX_UP    (GADGET_ID_COUNTER_FIRST + 35)
-#define GADGET_ID_PUSH_DELAY_RND_DOWN  (GADGET_ID_COUNTER_FIRST + 36)
-#define GADGET_ID_PUSH_DELAY_RND_TEXT  (GADGET_ID_COUNTER_FIRST + 37)
-#define GADGET_ID_PUSH_DELAY_RND_UP    (GADGET_ID_COUNTER_FIRST + 38)
-#define GADGET_ID_MOVE_DELAY_FIX_DOWN  (GADGET_ID_COUNTER_FIRST + 39)
-#define GADGET_ID_MOVE_DELAY_FIX_TEXT  (GADGET_ID_COUNTER_FIRST + 40)
-#define GADGET_ID_MOVE_DELAY_FIX_UP    (GADGET_ID_COUNTER_FIRST + 41)
-#define GADGET_ID_MOVE_DELAY_RND_DOWN  (GADGET_ID_COUNTER_FIRST + 42)
-#define GADGET_ID_MOVE_DELAY_RND_TEXT  (GADGET_ID_COUNTER_FIRST + 43)
-#define GADGET_ID_MOVE_DELAY_RND_UP    (GADGET_ID_COUNTER_FIRST + 44)
-#define GADGET_ID_CHANGE_DELAY_FIX_DOWN        (GADGET_ID_COUNTER_FIRST + 45)
-#define GADGET_ID_CHANGE_DELAY_FIX_TEXT        (GADGET_ID_COUNTER_FIRST + 46)
-#define GADGET_ID_CHANGE_DELAY_FIX_UP  (GADGET_ID_COUNTER_FIRST + 47)
-#define GADGET_ID_CHANGE_DELAY_RND_DOWN        (GADGET_ID_COUNTER_FIRST + 48)
-#define GADGET_ID_CHANGE_DELAY_RND_TEXT        (GADGET_ID_COUNTER_FIRST + 49)
-#define GADGET_ID_CHANGE_DELAY_RND_UP  (GADGET_ID_COUNTER_FIRST + 50)
-#define GADGET_ID_CHANGE_CONT_RND_DOWN (GADGET_ID_COUNTER_FIRST + 51)
-#define GADGET_ID_CHANGE_CONT_RND_TEXT (GADGET_ID_COUNTER_FIRST + 52)
-#define GADGET_ID_CHANGE_CONT_RND_UP   (GADGET_ID_COUNTER_FIRST + 53)
+#define GADGET_ID_ENVELOPE_XSIZE_DOWN  (GADGET_ID_COUNTER_FIRST + 27)
+#define GADGET_ID_ENVELOPE_XSIZE_TEXT  (GADGET_ID_COUNTER_FIRST + 28)
+#define GADGET_ID_ENVELOPE_XSIZE_UP    (GADGET_ID_COUNTER_FIRST + 29)
+#define GADGET_ID_ENVELOPE_YSIZE_DOWN  (GADGET_ID_COUNTER_FIRST + 30)
+#define GADGET_ID_ENVELOPE_YSIZE_TEXT  (GADGET_ID_COUNTER_FIRST + 31)
+#define GADGET_ID_ENVELOPE_YSIZE_UP    (GADGET_ID_COUNTER_FIRST + 32)
+#define GADGET_ID_CUSTOM_SCORE_DOWN    (GADGET_ID_COUNTER_FIRST + 33)
+#define GADGET_ID_CUSTOM_SCORE_TEXT    (GADGET_ID_COUNTER_FIRST + 34)
+#define GADGET_ID_CUSTOM_SCORE_UP      (GADGET_ID_COUNTER_FIRST + 35)
+#define GADGET_ID_CUSTOM_GEMCOUNT_DOWN (GADGET_ID_COUNTER_FIRST + 36)
+#define GADGET_ID_CUSTOM_GEMCOUNT_TEXT (GADGET_ID_COUNTER_FIRST + 37)
+#define GADGET_ID_CUSTOM_GEMCOUNT_UP   (GADGET_ID_COUNTER_FIRST + 38)
+#define GADGET_ID_PUSH_DELAY_FIX_DOWN  (GADGET_ID_COUNTER_FIRST + 39)
+#define GADGET_ID_PUSH_DELAY_FIX_TEXT  (GADGET_ID_COUNTER_FIRST + 40)
+#define GADGET_ID_PUSH_DELAY_FIX_UP    (GADGET_ID_COUNTER_FIRST + 41)
+#define GADGET_ID_PUSH_DELAY_RND_DOWN  (GADGET_ID_COUNTER_FIRST + 42)
+#define GADGET_ID_PUSH_DELAY_RND_TEXT  (GADGET_ID_COUNTER_FIRST + 43)
+#define GADGET_ID_PUSH_DELAY_RND_UP    (GADGET_ID_COUNTER_FIRST + 44)
+#define GADGET_ID_MOVE_DELAY_FIX_DOWN  (GADGET_ID_COUNTER_FIRST + 45)
+#define GADGET_ID_MOVE_DELAY_FIX_TEXT  (GADGET_ID_COUNTER_FIRST + 46)
+#define GADGET_ID_MOVE_DELAY_FIX_UP    (GADGET_ID_COUNTER_FIRST + 47)
+#define GADGET_ID_MOVE_DELAY_RND_DOWN  (GADGET_ID_COUNTER_FIRST + 48)
+#define GADGET_ID_MOVE_DELAY_RND_TEXT  (GADGET_ID_COUNTER_FIRST + 49)
+#define GADGET_ID_MOVE_DELAY_RND_UP    (GADGET_ID_COUNTER_FIRST + 50)
+#define GADGET_ID_CHANGE_DELAY_FIX_DOWN        (GADGET_ID_COUNTER_FIRST + 51)
+#define GADGET_ID_CHANGE_DELAY_FIX_TEXT        (GADGET_ID_COUNTER_FIRST + 52)
+#define GADGET_ID_CHANGE_DELAY_FIX_UP  (GADGET_ID_COUNTER_FIRST + 53)
+#define GADGET_ID_CHANGE_DELAY_RND_DOWN        (GADGET_ID_COUNTER_FIRST + 54)
+#define GADGET_ID_CHANGE_DELAY_RND_TEXT        (GADGET_ID_COUNTER_FIRST + 55)
+#define GADGET_ID_CHANGE_DELAY_RND_UP  (GADGET_ID_COUNTER_FIRST + 56)
+#define GADGET_ID_CHANGE_CONT_RND_DOWN (GADGET_ID_COUNTER_FIRST + 57)
+#define GADGET_ID_CHANGE_CONT_RND_TEXT (GADGET_ID_COUNTER_FIRST + 58)
+#define GADGET_ID_CHANGE_CONT_RND_UP   (GADGET_ID_COUNTER_FIRST + 59)
 
 /* drawing area identifiers */
-#define GADGET_ID_DRAWING_AREA_FIRST   (GADGET_ID_COUNTER_FIRST + 54)
+#define GADGET_ID_DRAWING_AREA_FIRST   (GADGET_ID_COUNTER_FIRST + 60)
 
 #define GADGET_ID_DRAWING_LEVEL                (GADGET_ID_DRAWING_AREA_FIRST + 0)
 #define GADGET_ID_ELEMENT_CONTENT_0    (GADGET_ID_DRAWING_AREA_FIRST + 1)
 #define GADGET_ID_LEVEL_AUTHOR         (GADGET_ID_TEXT_INPUT_FIRST + 1)
 #define GADGET_ID_ELEMENT_NAME         (GADGET_ID_TEXT_INPUT_FIRST + 2)
 
+/* text area identifiers */
+#define GADGET_ID_TEXT_AREA_FIRST      (GADGET_ID_TEXT_INPUT_FIRST + 3)
+
+#define GADGET_ID_ENVELOPE_INFO                (GADGET_ID_TEXT_AREA_FIRST + 0)
+
 /* selectbox identifiers */
-#define GADGET_ID_SELECTBOX_FIRST      (GADGET_ID_TEXT_INPUT_FIRST + 3)
+#define GADGET_ID_SELECTBOX_FIRST      (GADGET_ID_TEXT_AREA_FIRST + 1)
 
 #define GADGET_ID_CUSTOM_WALK_TO_ACTION        (GADGET_ID_SELECTBOX_FIRST + 0)
 #define GADGET_ID_CUSTOM_CONSISTENCY   (GADGET_ID_SELECTBOX_FIRST + 1)
 #define GADGET_ID_CUSTOM_ACCESS_TYPE   (GADGET_ID_SELECTBOX_FIRST + 8)
 #define GADGET_ID_CUSTOM_ACCESS_LAYER  (GADGET_ID_SELECTBOX_FIRST + 9)
 #define GADGET_ID_CHANGE_TIME_UNITS    (GADGET_ID_SELECTBOX_FIRST + 10)
-#define GADGET_ID_CHANGE_PLAYER_ACTION (GADGET_ID_SELECTBOX_FIRST + 11)
-#define GADGET_ID_CHANGE_COLLIDE_ACTION        (GADGET_ID_SELECTBOX_FIRST + 12)
-#define GADGET_ID_CHANGE_OTHER_ACTION  (GADGET_ID_SELECTBOX_FIRST + 13)
-#define GADGET_ID_CHANGE_POWER         (GADGET_ID_SELECTBOX_FIRST + 14)
+#define GADGET_ID_CHANGE_DIRECT_ACTION (GADGET_ID_SELECTBOX_FIRST + 11)
+#define GADGET_ID_CHANGE_OTHER_ACTION  (GADGET_ID_SELECTBOX_FIRST + 12)
+#define GADGET_ID_CHANGE_POWER         (GADGET_ID_SELECTBOX_FIRST + 13)
+#define GADGET_ID_SELECT_CHANGE_PAGE   (GADGET_ID_SELECTBOX_FIRST + 14)
 
 /* textbutton identifiers */
 #define GADGET_ID_TEXTBUTTON_FIRST     (GADGET_ID_SELECTBOX_FIRST + 15)
 #define GADGET_ID_PROPERTIES_CONFIG    (GADGET_ID_TEXTBUTTON_FIRST + 1)
 #define GADGET_ID_PROPERTIES_ADVANCED  (GADGET_ID_TEXTBUTTON_FIRST + 2)
 #define GADGET_ID_SAVE_AS_TEMPLATE     (GADGET_ID_TEXTBUTTON_FIRST + 3)
+#define GADGET_ID_ADD_CHANGE_PAGE      (GADGET_ID_TEXTBUTTON_FIRST + 4)
+#define GADGET_ID_DEL_CHANGE_PAGE      (GADGET_ID_TEXTBUTTON_FIRST + 5)
+
+/* graphicbutton identifiers */
+#define GADGET_ID_GRAPHICBUTTON_FIRST  (GADGET_ID_TEXTBUTTON_FIRST + 6)
+
+#define GADGET_ID_PREV_CHANGE_PAGE     (GADGET_ID_GRAPHICBUTTON_FIRST + 0)
+#define GADGET_ID_NEXT_CHANGE_PAGE     (GADGET_ID_GRAPHICBUTTON_FIRST + 1)
 
 /* gadgets for scrolling of drawing area */
-#define GADGET_ID_SCROLLING_FIRST      (GADGET_ID_TEXTBUTTON_FIRST + 4)
+#define GADGET_ID_SCROLLING_FIRST      (GADGET_ID_GRAPHICBUTTON_FIRST + 2)
 
 #define GADGET_ID_SCROLL_UP            (GADGET_ID_SCROLLING_FIRST + 0)
 #define GADGET_ID_SCROLL_DOWN          (GADGET_ID_SCROLLING_FIRST + 1)
 #define GADGET_ID_CHANGE_ONLY_COMPLETE (GADGET_ID_CHECKBUTTON_FIRST + 23)
 #define GADGET_ID_CHANGE_USE_RANDOM    (GADGET_ID_CHECKBUTTON_FIRST + 24)
 #define GADGET_ID_CHANGE_DELAY         (GADGET_ID_CHECKBUTTON_FIRST + 25)
-#define GADGET_ID_CHANGE_BY_PLAYER     (GADGET_ID_CHECKBUTTON_FIRST + 26)
-#define GADGET_ID_CHANGE_BY_COLLISION  (GADGET_ID_CHECKBUTTON_FIRST + 27)
-#define GADGET_ID_CHANGE_BY_OTHER      (GADGET_ID_CHECKBUTTON_FIRST + 28)
+#define GADGET_ID_CHANGE_BY_DIRECT_ACT (GADGET_ID_CHECKBUTTON_FIRST + 26)
+#define GADGET_ID_CHANGE_BY_OTHER_ACT  (GADGET_ID_CHECKBUTTON_FIRST + 27)
 
 /* gadgets for buttons in element list */
-#define GADGET_ID_ELEMENTLIST_FIRST    (GADGET_ID_CHECKBUTTON_FIRST + 29)
+#define GADGET_ID_ELEMENTLIST_FIRST    (GADGET_ID_CHECKBUTTON_FIRST + 28)
 #define GADGET_ID_ELEMENTLIST_LAST     (GADGET_ID_ELEMENTLIST_FIRST +  \
                                        ED_NUM_ELEMENTLIST_BUTTONS - 1)
 
 #define ED_COUNTER_ID_LEVEL_RANDOM     6
 #define ED_COUNTER_ID_ELEMENT_SCORE    7
 #define ED_COUNTER_ID_ELEMENT_CONTENT  8
-#define ED_COUNTER_ID_CUSTOM_SCORE     9
-#define ED_COUNTER_ID_CUSTOM_GEMCOUNT  10
-#define ED_COUNTER_ID_PUSH_DELAY_FIX   11
-#define ED_COUNTER_ID_PUSH_DELAY_RND   12
-#define ED_COUNTER_ID_MOVE_DELAY_FIX   13
-#define ED_COUNTER_ID_MOVE_DELAY_RND   14
-#define ED_COUNTER_ID_CHANGE_DELAY_FIX 15
-#define ED_COUNTER_ID_CHANGE_DELAY_RND 16
-#define ED_COUNTER_ID_CHANGE_CONT_RND  17
-
-#define ED_NUM_COUNTERBUTTONS          18
+#define ED_COUNTER_ID_ENVELOPE_XSIZE   9
+#define ED_COUNTER_ID_ENVELOPE_YSIZE   10
+#define ED_COUNTER_ID_CUSTOM_SCORE     11
+#define ED_COUNTER_ID_CUSTOM_GEMCOUNT  12
+#define ED_COUNTER_ID_PUSH_DELAY_FIX   13
+#define ED_COUNTER_ID_PUSH_DELAY_RND   14
+#define ED_COUNTER_ID_MOVE_DELAY_FIX   15
+#define ED_COUNTER_ID_MOVE_DELAY_RND   16
+#define ED_COUNTER_ID_CHANGE_DELAY_FIX 17
+#define ED_COUNTER_ID_CHANGE_DELAY_RND 18
+#define ED_COUNTER_ID_CHANGE_CONT_RND  19
+
+#define ED_NUM_COUNTERBUTTONS          20
 
 #define ED_COUNTER_ID_LEVEL_FIRST      ED_COUNTER_ID_LEVEL_XSIZE
 #define ED_COUNTER_ID_LEVEL_LAST       ED_COUNTER_ID_LEVEL_RANDOM
 #define ED_TEXTINPUT_ID_LEVEL_FIRST    ED_TEXTINPUT_ID_LEVEL_NAME
 #define ED_TEXTINPUT_ID_LEVEL_LAST     ED_TEXTINPUT_ID_LEVEL_AUTHOR
 
+/* values for text area gadgets */
+#define ED_TEXTAREA_ID_ENVELOPE_INFO   0
+
+#define ED_NUM_TEXTAREAS                       1
+
+#define ED_TEXTAREA_ID_LEVEL_FIRST     ED_TEXTAREA_ID_ENVELOPE
+#define ED_TEXTAREA_ID_LEVEL_LAST      ED_TEXTAREA_ID_ENVELOPE
+
 /* values for selectbox gadgets */
 #define ED_SELECTBOX_ID_CUSTOM_ACCESS_TYPE     0
 #define ED_SELECTBOX_ID_CUSTOM_ACCESS_LAYER    1
 #define ED_SELECTBOX_ID_CUSTOM_DEADLINESS      8
 #define ED_SELECTBOX_ID_CUSTOM_CONSISTENCY     9
 #define ED_SELECTBOX_ID_CHANGE_TIME_UNITS      10
-#define ED_SELECTBOX_ID_CHANGE_PLAYER_ACTION   11
-#define ED_SELECTBOX_ID_CHANGE_COLLIDE_ACTION  12
-#define ED_SELECTBOX_ID_CHANGE_OTHER_ACTION    13
-#define ED_SELECTBOX_ID_CHANGE_POWER           14
+#define ED_SELECTBOX_ID_CHANGE_DIRECT_ACTION   11
+#define ED_SELECTBOX_ID_CHANGE_OTHER_ACTION    12
+#define ED_SELECTBOX_ID_CHANGE_POWER           13
+#define ED_SELECTBOX_ID_SELECT_CHANGE_PAGE     14
 
 #define ED_NUM_SELECTBOX                       15
 
 #define ED_SELECTBOX_ID_CUSTOM_LAST    ED_SELECTBOX_ID_CUSTOM_CONSISTENCY
 
 #define ED_SELECTBOX_ID_CHANGE_FIRST   ED_SELECTBOX_ID_CHANGE_TIME_UNITS
-#define ED_SELECTBOX_ID_CHANGE_LAST    ED_SELECTBOX_ID_CHANGE_POWER
+#define ED_SELECTBOX_ID_CHANGE_LAST    ED_SELECTBOX_ID_SELECT_CHANGE_PAGE
 
 /* values for textbutton gadgets */
 #define ED_TEXTBUTTON_ID_PROPERTIES_INFO       0
 #define ED_TEXTBUTTON_ID_PROPERTIES_CONFIG     1
 #define ED_TEXTBUTTON_ID_PROPERTIES_ADVANCED   2
 #define ED_TEXTBUTTON_ID_SAVE_AS_TEMPLATE      3
+#define ED_TEXTBUTTON_ID_ADD_CHANGE_PAGE       4
+#define ED_TEXTBUTTON_ID_DEL_CHANGE_PAGE       5
+
+#define ED_NUM_TEXTBUTTONS                     6
+
+#define ED_TEXTBUTTON_ID_PROPERTIES_FIRST ED_TEXTBUTTON_ID_PROPERTIES_INFO
+#define ED_TEXTBUTTON_ID_PROPERTIES_LAST  ED_TEXTBUTTON_ID_PROPERTIES_ADVANCED
+
+#define ED_TEXTBUTTON_ID_CHANGE_FIRST  ED_TEXTBUTTON_ID_SAVE_AS_TEMPLATE
+#define ED_TEXTBUTTON_ID_CHANGE_LAST   ED_TEXTBUTTON_ID_DEL_CHANGE_PAGE
+
+/* values for graphicbutton gadgets */
+#define ED_GRAPHICBUTTON_ID_PREV_CHANGE_PAGE   0
+#define ED_GRAPHICBUTTON_ID_NEXT_CHANGE_PAGE   1
 
-#define ED_NUM_TEXTBUTTON                      4
+#define ED_NUM_GRAPHICBUTTONS                  2
+
+#define ED_GRAPHICBUTTON_ID_CHANGE_FIRST   ED_GRAPHICBUTTON_ID_PREV_CHANGE_PAGE
+#define ED_GRAPHICBUTTON_ID_CHANGE_LAST    ED_GRAPHICBUTTON_ID_NEXT_CHANGE_PAGE
 
 /* values for checkbutton gadgets */
 #define ED_CHECKBUTTON_ID_DOUBLE_SPEED         0
 #define ED_CHECKBUTTON_ID_CUSTOM_USE_GRAPHIC   16
 #define ED_CHECKBUTTON_ID_CUSTOM_CAN_CHANGE    17
 #define ED_CHECKBUTTON_ID_CHANGE_DELAY         18
-#define ED_CHECKBUTTON_ID_CHANGE_BY_PLAYER     19
-#define ED_CHECKBUTTON_ID_CHANGE_BY_COLLISION  20
-#define ED_CHECKBUTTON_ID_CHANGE_BY_OTHER      21
-#define ED_CHECKBUTTON_ID_CHANGE_USE_EXPLOSION 22
-#define ED_CHECKBUTTON_ID_CHANGE_USE_CONTENT   23
-#define ED_CHECKBUTTON_ID_CHANGE_ONLY_COMPLETE 24
-#define ED_CHECKBUTTON_ID_CHANGE_USE_RANDOM    25
-#define ED_CHECKBUTTON_ID_CUSTOM_USE_TEMPLATE  26
+#define ED_CHECKBUTTON_ID_CHANGE_BY_DIRECT_ACT 19
+#define ED_CHECKBUTTON_ID_CHANGE_BY_OTHER_ACT  20
+#define ED_CHECKBUTTON_ID_CHANGE_USE_EXPLOSION 21
+#define ED_CHECKBUTTON_ID_CHANGE_USE_CONTENT   22
+#define ED_CHECKBUTTON_ID_CHANGE_ONLY_COMPLETE 23
+#define ED_CHECKBUTTON_ID_CHANGE_USE_RANDOM    24
+#define ED_CHECKBUTTON_ID_CUSTOM_USE_TEMPLATE  25
 
-#define ED_NUM_CHECKBUTTONS                    27
+#define ED_NUM_CHECKBUTTONS                    26
 
 #define ED_CHECKBUTTON_ID_LEVEL_FIRST  ED_CHECKBUTTON_ID_DOUBLE_SPEED
 #define ED_CHECKBUTTON_ID_LEVEL_LAST   ED_CHECKBUTTON_ID_RANDOM_RESTRICTED
@@ -726,6 +762,7 @@ static boolean random_placement_background_restricted = FALSE;
 static boolean stick_element_properties_window = FALSE;
 static boolean custom_element_properties[NUM_ELEMENT_PROPERTIES];
 static boolean custom_element_change_events[NUM_CHANGE_EVENTS];
+static struct ElementChangeInfo custom_element_change;
 static struct ElementInfo custom_element;
 
 static struct
@@ -758,7 +795,7 @@ static struct
     "playfield size:",                 NULL, "width",
   },
   {
-    ED_SETTINGS_XPOS(0) + 2 * DXSIZE,  ED_COUNTER_YPOS(2),
+    -1,                                        ED_COUNTER_YPOS(2),
     MIN_LEV_FIELDY,                    MAX_LEV_FIELDY,
     GADGET_ID_LEVEL_YSIZE_DOWN,                GADGET_ID_LEVEL_YSIZE_UP,
     GADGET_ID_LEVEL_YSIZE_TEXT,                GADGET_ID_LEVEL_XSIZE_UP,
@@ -816,6 +853,22 @@ static struct
     &level.num_yamyam_contents,
     NULL,                              NULL, "number of content areas"
   },
+  {
+    ED_SETTINGS_XPOS(0),               ED_SETTINGS_YPOS(0),
+    MIN_ENVELOPE_XSIZE,                        MAX_ENVELOPE_XSIZE,
+    GADGET_ID_ENVELOPE_XSIZE_DOWN,     GADGET_ID_ENVELOPE_XSIZE_UP,
+    GADGET_ID_ENVELOPE_XSIZE_TEXT,     GADGET_ID_NONE,
+    &level.envelope_xsize,
+    NULL,                              NULL, "width",
+  },
+  {
+    -1,                                        ED_SETTINGS_YPOS(0),
+    MIN_ENVELOPE_YSIZE,                        MAX_ENVELOPE_YSIZE,
+    GADGET_ID_ENVELOPE_YSIZE_DOWN,     GADGET_ID_ENVELOPE_YSIZE_UP,
+    GADGET_ID_ENVELOPE_YSIZE_TEXT,     GADGET_ID_ENVELOPE_XSIZE_UP,
+    &level.envelope_ysize,
+    NULL,                              " ", "height",
+  },
 
   /* ---------- element settings: configure (custom elements) ------------- */
 
@@ -825,10 +878,10 @@ static struct
     GADGET_ID_CUSTOM_SCORE_DOWN,       GADGET_ID_CUSTOM_SCORE_UP,
     GADGET_ID_CUSTOM_SCORE_TEXT,       GADGET_ID_NONE,
     &custom_element.collect_score,
-    NULL,                              "score", NULL
+    NULL,                              "score", " "
   },
   {
-    ED_SETTINGS_XPOS(13) + 10,         ED_SETTINGS_YPOS(3),
+    -1,                                        ED_SETTINGS_YPOS(3),
     MIN_COLLECT_COUNT,                 MAX_COLLECT_COUNT,
     GADGET_ID_CUSTOM_GEMCOUNT_DOWN,    GADGET_ID_CUSTOM_GEMCOUNT_UP,
     GADGET_ID_CUSTOM_GEMCOUNT_TEXT,    GADGET_ID_CUSTOM_SCORE_UP,
@@ -844,7 +897,7 @@ static struct
     NULL,                              "push delay", NULL
   },
   {
-    ED_COUNT_PUSH_DELAY_RND_XPOS,      ED_SETTINGS_YPOS(4),
+    -1,                                        ED_SETTINGS_YPOS(4),
     0,                                 999,
     GADGET_ID_PUSH_DELAY_RND_DOWN,     GADGET_ID_PUSH_DELAY_RND_UP,
     GADGET_ID_PUSH_DELAY_RND_TEXT,     GADGET_ID_PUSH_DELAY_FIX_UP,
@@ -860,7 +913,7 @@ static struct
     NULL,                              "move delay", NULL
   },
   {
-    ED_COUNT_MOVE_DELAY_RND_XPOS,      ED_SETTINGS_YPOS(7),
+    -1,                                        ED_SETTINGS_YPOS(7),
     0,                                 999,
     GADGET_ID_MOVE_DELAY_RND_DOWN,     GADGET_ID_MOVE_DELAY_RND_UP,
     GADGET_ID_MOVE_DELAY_RND_TEXT,     GADGET_ID_MOVE_DELAY_FIX_UP,
@@ -875,23 +928,23 @@ static struct
     0,                                 999,
     GADGET_ID_CHANGE_DELAY_FIX_DOWN,   GADGET_ID_CHANGE_DELAY_FIX_UP,
     GADGET_ID_CHANGE_DELAY_FIX_TEXT,   GADGET_ID_NONE,
-    &custom_element.change.delay_fixed,
+    &custom_element_change.delay_fixed,
     NULL,                              "delay", NULL,
   },
   {
-    ED_COUNT_CHANGE_DELAY_RND_XPOS+20, ED_SETTINGS_YPOS(3),
+    -1,                                        ED_SETTINGS_YPOS(3),
     0,                                 999,
     GADGET_ID_CHANGE_DELAY_RND_DOWN,   GADGET_ID_CHANGE_DELAY_RND_UP,
     GADGET_ID_CHANGE_DELAY_RND_TEXT,   GADGET_ID_CHANGE_DELAY_FIX_UP,
-    &custom_element.change.delay_random,
+    &custom_element_change.delay_random,
     NULL,                              "+random", NULL
   },
   {
-    ED_SETTINGS_XPOS(3),               ED_SETTINGS_YPOS(12),
+    ED_SETTINGS_XPOS(3),               ED_SETTINGS_YPOS(11),
     0,                                 100,
     GADGET_ID_CHANGE_CONT_RND_DOWN,    GADGET_ID_CHANGE_CONT_RND_UP,
     GADGET_ID_CHANGE_CONT_RND_TEXT,    GADGET_ID_NONE,
-    &custom_element.change.random,
+    &custom_element_change.random,
     NULL,                              "use random change:", "(%)"
   },
 };
@@ -902,7 +955,7 @@ static struct
   int gadget_id;
   int size;
   char *value;
-  char *infotext;
+  char *text_above, *infotext;
 } textinput_info[ED_NUM_TEXTINPUT] =
 {
   {
@@ -910,21 +963,39 @@ static struct
     GADGET_ID_LEVEL_NAME,
     MAX_LEVEL_NAME_LEN,
     level.name,
-    "Title"
+    "Title:", "Title"
   },
   {
     ED_SETTINGS_XPOS(0),               ED_COUNTER_YPOS(1),
     GADGET_ID_LEVEL_AUTHOR,
     MAX_LEVEL_AUTHOR_LEN,
     level.author,
-    "Author"
+    "Author:", "Author"
   },
   {
     5 * MINI_TILEX,                    5 * MINI_TILEY - ED_BORDER_SIZE,
     GADGET_ID_ELEMENT_NAME,
     MAX_ELEMENT_NAME_LEN - 2,          /* currently 2 chars less editable */
     custom_element.description,
-    "Element name"
+    NULL, "Element name"
+  }
+};
+
+static struct
+{
+  int x, y;
+  int gadget_id;
+  int xsize, ysize;
+  char *value;
+  char *text_above, *infotext;
+} textarea_info[ED_NUM_TEXTAREAS] =
+{
+  {
+    ED_SETTINGS_XPOS(0),               ED_SETTINGS_YPOS(2),
+    GADGET_ID_ENVELOPE_INFO,
+    MAX_ENVELOPE_XSIZE, MAX_ENVELOPE_YSIZE,
+    level.envelope,
+    "Envelope Info:", "Envelope Info"
   }
 };
 
@@ -1028,46 +1099,48 @@ static struct ValueTextInfo options_consistency[] =
 
 static struct ValueTextInfo options_time_units[] =
 {
-  { 50,                                "seconds"                       },
+  { FRAMES_PER_SECOND,         "seconds"                       },
   { 1,                         "frames"                        },
   { -1,                                NULL                            }
 };
 
-static struct ValueTextInfo options_change_player_action[] =
-{
-  { CE_TOUCHED_BY_PLAYER,      "touched"                       },
-  { CE_PRESSED_BY_PLAYER,      "pressed"                       },
-  { CE_PUSHED_BY_PLAYER,       "pushed"                        },
-  { CE_DROPPED_BY_PLAYER,      "dropped"                       },
-  { -1,                                NULL                            }
-};
-
-static struct ValueTextInfo options_change_collide_action[] =
+static struct ValueTextInfo options_change_direct_action[] =
 {
-  { CE_COLLISION,              "on collision"                  },
-  { CE_IMPACT,                 "on impact"                     },
-  { CE_SMASHED,                        "when smashed"                  },
+  { CE_TOUCHED_BY_PLAYER,      "touched by player"             },
+  { CE_PRESSED_BY_PLAYER,      "pressed by player"             },
+  { CE_PUSHED_BY_PLAYER,       "pushed by player"              },
+  { CE_DROPPED_BY_PLAYER,      "dropped by player"             },
+  { CE_COLLISION,              "collision"                     },
+  { CE_IMPACT,                 "impact"                        },
+  { CE_SMASHED,                        "smashed"                       },
   { -1,                                NULL                            }
 };
 
 static struct ValueTextInfo options_change_other_action[] =
 {
-  { CE_OTHER_IS_TOUCHING,      "touching"                      },
-  { CE_OTHER_IS_CHANGING,      "change of"                     },
-  { CE_OTHER_IS_EXPLODING,     "explosion of"                  },
   { CE_OTHER_GETS_TOUCHED,     "player touches"                },
   { CE_OTHER_GETS_PRESSED,     "player presses"                },
   { CE_OTHER_GETS_PUSHED,      "player pushes"                 },
+  { CE_OTHER_GETS_DIGGED,      "player digs"                   },
   { CE_OTHER_GETS_COLLECTED,   "player collects"               },
   { CE_OTHER_GETS_DROPPED,     "player drops"                  },
+  { CE_OTHER_IS_TOUCHING,      "touching"                      },
+  { CE_OTHER_IS_CHANGING,      "change of"                     },
+  { CE_OTHER_IS_EXPLODING,     "explosion of"                  },
   { -1,                                NULL                            }
 };
 
 static struct ValueTextInfo options_change_power[] =
 {
-  { CP_NON_DESTRUCTIVE,                "non-destructive"               },
-  { CP_HALF_DESTRUCTIVE,       "half-destructive"              },
-  { CP_FULL_DESTRUCTIVE,       "full-destructive"              },
+  { CP_NON_DESTRUCTIVE,                "empty"                         },
+  { CP_HALF_DESTRUCTIVE,       "diggable"                      },
+  { CP_FULL_DESTRUCTIVE,       "destructible"                  },
+  { -1,                                NULL                            }
+};
+
+static char options_change_page_strings[MAX_CHANGE_PAGES][10];
+static struct ValueTextInfo options_change_page[MAX_CHANGE_PAGES + 1] =
+{
   { -1,                                NULL                            }
 };
 
@@ -1090,15 +1163,15 @@ static struct
     -1,
     options_access_type,
     &custom_element.access_type,
-    "player can", NULL, "type of access to this field"
+    "player can", NULL,                        "type of access to this field"
   },
   {
-    ED_SETTINGS_XPOS(11),              ED_SETTINGS_YPOS(1),
+    -1,                                        ED_SETTINGS_YPOS(1),
     GADGET_ID_CUSTOM_ACCESS_LAYER,     GADGET_ID_CUSTOM_ACCESS_TYPE,
     -1,
     options_access_layer,
     &custom_element.access_layer,
-    NULL, NULL, "layer of access for this field"
+    NULL, NULL,                                "layer of access for this field"
   },
   {
     ED_SETTINGS_XPOS(1),               ED_SETTINGS_YPOS(2),
@@ -1106,7 +1179,7 @@ static struct
     -1,
     options_walk_to_action,
     &custom_element.walk_to_action,
-    NULL, NULL, "diggable/collectible/pushable"
+    NULL, NULL,                                "diggable/collectible/pushable"
   },
   {
     ED_SETTINGS_XPOS(1),               ED_SETTINGS_YPOS(5),
@@ -1114,7 +1187,7 @@ static struct
     -1,
     options_move_pattern,
     &custom_element.move_pattern,
-    "can move", NULL, "element move direction"
+    "can move", NULL,                  "element move direction"
   },
   {
     ED_SETTINGS_XPOS(1),               ED_SETTINGS_YPOS(6),
@@ -1122,7 +1195,7 @@ static struct
     -1,
     options_move_direction,
     &custom_element.move_direction_initial,
-    "starts moving", NULL, "initial element move direction"
+    "starts moving", NULL,             "initial element move direction"
   },
   {
     ED_SETTINGS_XPOS(1),               ED_SETTINGS_YPOS(8),
@@ -1130,15 +1203,15 @@ static struct
     -1,
     options_move_stepsize,
     &custom_element.move_stepsize,
-    "move/fall speed", NULL, "speed of element movement"
+    "move/fall speed", NULL,           "speed of element movement"
   },
   {
-    ED_SETTINGS_XPOS(7),               ED_SETTINGS_YPOS(9),
+    -1,                                        ED_SETTINGS_YPOS(9),
     GADGET_ID_CUSTOM_SMASH_TARGETS,    GADGET_ID_CUSTOM_CAN_SMASH,
     -1,
     options_smash_targets,
     &custom_element.smash_targets,
-    "can smash", NULL, "elements that can be smashed"
+    "can smash", NULL,                 "elements that can be smashed"
   },
   {
     ED_SETTINGS_XPOS(1),               ED_SETTINGS_YPOS(10),
@@ -1146,7 +1219,7 @@ static struct
     -1,
     options_slippery_type,
     &custom_element.slippery_type,
-    "slippery", NULL, "where other elements fall down"
+    "slippery", NULL,                  "where other elements fall down"
   },
   {
     ED_SETTINGS_XPOS(1),               ED_SETTINGS_YPOS(11),
@@ -1154,7 +1227,7 @@ static struct
     -1,
     options_deadliness,
     &custom_element.deadliness,
-    "deadly when", NULL, "deadliness of element"
+    "deadly when", NULL,               "deadliness of element"
   },
   {
     ED_SETTINGS_XPOS(1),               ED_SETTINGS_YPOS(12),
@@ -1162,7 +1235,7 @@ static struct
     -1,
     options_consistency,
     &custom_element.consistency,
-    NULL, NULL, "consistency/destructibility"
+    NULL, NULL,                                "consistency/destructibility"
   },
 
   /* ---------- element settings: advanced (custom elements) --------------- */
@@ -1172,40 +1245,40 @@ static struct
     GADGET_ID_CHANGE_TIME_UNITS,       GADGET_ID_NONE,
     -1,
     options_time_units,
-    &custom_element.change.delay_frames,
-    "delay time given in", NULL, "delay time units for change"
+    &custom_element_change.delay_frames,
+    "delay time given in", NULL,       "delay time units for change"
   },
   {
     ED_SETTINGS_XPOS(2),               ED_SETTINGS_YPOS(5),
-    GADGET_ID_CHANGE_PLAYER_ACTION,    GADGET_ID_NONE,
+    GADGET_ID_CHANGE_DIRECT_ACTION,    GADGET_ID_NONE,
     -1,
-    options_change_player_action,
-    &custom_element.change_player_action,
-    NULL, "by player", "type of player contact"
+    options_change_direct_action,
+    &custom_element_change.direct_action,
+    NULL, NULL,                                "type of direct action"
   },
   {
     ED_SETTINGS_XPOS(2),               ED_SETTINGS_YPOS(6),
-    GADGET_ID_CHANGE_COLLIDE_ACTION,   GADGET_ID_NONE,
-    -1,
-    options_change_collide_action,
-    &custom_element.change_collide_action,
-    NULL, NULL, "change after impact or smash"
-  },
-  {
-    ED_SETTINGS_XPOS(2),               ED_SETTINGS_YPOS(7),
     GADGET_ID_CHANGE_OTHER_ACTION,     GADGET_ID_NONE,
     -1,
     options_change_other_action,
-    &custom_element.change_other_action,
-    NULL, "element:", "type of other element action"
+    &custom_element_change.other_action,
+    NULL, "element:",                  "type of other element action"
   },
   {
-    ED_SETTINGS_XPOS(2),               ED_SETTINGS_YPOS(10),
+    ED_SETTINGS_XPOS(2),               ED_SETTINGS_YPOS(9),
     GADGET_ID_CHANGE_POWER,            GADGET_ID_NONE,
     -1,
     options_change_power,
-    &custom_element.change.power,
-    "power:", NULL, "power of extended change"
+    &custom_element_change.power,
+    "replace when", NULL,              "which elements can be replaced"
+  },
+  {
+    ED_SETTINGS_XPOS(1),               ED_SETTINGS_YPOS(13),
+    GADGET_ID_SELECT_CHANGE_PAGE,      GADGET_ID_NONE,
+    3,
+    options_change_page,
+    &custom_element.current_change_page,
+    NULL, NULL,                                "element config page"
   },
 };
 
@@ -1215,28 +1288,71 @@ static struct
   int gadget_id;
   int gadget_id_align;
   int size;
-  char *text, *infotext;
-} textbutton_info[ED_NUM_TEXTBUTTON] =
+  char *text;
+  char *text_left, *text_right, *infotext;
+} textbutton_info[ED_NUM_TEXTBUTTONS] =
 {
   {
     ED_SETTINGS_XPOS(0),               ED_COUNTER_YPOS(1),
     GADGET_ID_PROPERTIES_INFO,         GADGET_ID_NONE,
-    11, "Information",                 "Show information about element"
+    11,                                        "Information",                  
+    NULL, NULL,                                "Show information about element"
   },
   {
     ED_SETTINGS_XPOS(0) + 166,         ED_COUNTER_YPOS(1),
     GADGET_ID_PROPERTIES_CONFIG,       GADGET_ID_NONE,
-    11, "Configure",                   "Configure element properties"
+    11,                                        "Configure",
+    NULL, NULL,                                "Configure element properties"
   },
   {
     ED_SETTINGS_XPOS(0) + 332,         ED_COUNTER_YPOS(1),
     GADGET_ID_PROPERTIES_ADVANCED,     GADGET_ID_NONE,
-    11, "Advanced",                    "Advanced element configuration"
+    11,                                        "Advanced",
+    NULL, NULL,                                "Advanced element configuration"
   },
   {
-    ED_SETTINGS_XPOS(0) + 262,         ED_SETTINGS_YPOS(13),
+    -1,                                        ED_SETTINGS_YPOS(12),
     GADGET_ID_SAVE_AS_TEMPLATE,                GADGET_ID_CUSTOM_USE_TEMPLATE,
-    -1, "Save as template",            "Save current settings as new template"
+    -1,                                        "Save as template",
+    " ", NULL,                         "Save current settings as new template"
+  },
+  {
+    -1,                                        ED_SETTINGS_YPOS(13),
+    GADGET_ID_ADD_CHANGE_PAGE,         GADGET_ID_NEXT_CHANGE_PAGE,
+    -1,                                        "New",
+    " ", NULL,                         "Add new config page"
+  },
+  {
+    -1,                                        ED_SETTINGS_YPOS(13),
+    GADGET_ID_DEL_CHANGE_PAGE,         GADGET_ID_ADD_CHANGE_PAGE,
+    -1,                                        "Delete",
+    NULL, NULL,                                "Delete current config page"
+  },
+};
+
+static struct
+{
+  int gd_x, gd_y;
+  int x, y;
+  int width, height;
+  int gadget_id;
+  int gadget_id_align;
+  char *text_left, *text_right, *infotext;
+} graphicbutton_info[ED_NUM_GRAPHICBUTTONS] =
+{
+  {
+    ED_BUTTON_MINUS_XPOS,              ED_BUTTON_COUNT_YPOS,
+    ED_SETTINGS_XPOS(0),               ED_SETTINGS_YPOS(13),
+    ED_BUTTON_COUNT_XSIZE,             ED_BUTTON_COUNT_YSIZE,
+    GADGET_ID_PREV_CHANGE_PAGE,                GADGET_ID_NONE,
+    NULL, NULL,                                "select previous config page"
+  },
+  {
+    ED_BUTTON_PLUS_XPOS,               ED_BUTTON_COUNT_YPOS,
+    -1,                                        ED_SETTINGS_YPOS(13),
+    ED_BUTTON_COUNT_XSIZE,             ED_BUTTON_COUNT_YSIZE,
+    GADGET_ID_NEXT_CHANGE_PAGE,                GADGET_ID_SELECT_CHANGE_PAGE,
+    NULL, "config page",               "select next config page"
   },
 };
 
@@ -1280,10 +1396,10 @@ static struct
   },
   {
     ED_SCROLLBUTTON2_XPOS,  ED_SCROLLBUTTON2_YPOS + 1 * ED_SCROLLBUTTON2_YSIZE,
-    ED_SCROLL2_DOWN_XPOS,    ED_SCROLL2_DOWN_YPOS,
+    ED_SCROLL2_DOWN_XPOS,   ED_SCROLL2_DOWN_YPOS,
     GADGET_ID_SCROLL_LIST_DOWN,
     "scroll element list down ('Page Down')"
-  }
+  },
 };
 
 static struct
@@ -1334,14 +1450,14 @@ static struct
 } radiobutton_info[ED_NUM_RADIOBUTTONS] =
 {
   {
-    ED_SETTINGS_XPOS(0) + 160,         ED_COUNTER2_YPOS(8),
+    -1,                                        ED_COUNTER2_YPOS(8),
     GADGET_ID_RANDOM_PERCENTAGE,       GADGET_ID_LEVEL_RANDOM_UP,
     RADIO_NR_RANDOM_ELEMENTS,
     &random_placement_method,          RANDOM_USE_PERCENTAGE,
     " ", "percentage",                 "use percentage for random elements"
   },
   {
-    ED_SETTINGS_XPOS(0) + 340,         ED_COUNTER2_YPOS(8),
+    -1,                                        ED_COUNTER2_YPOS(8),
     GADGET_ID_RANDOM_QUANTITY,         GADGET_ID_RANDOM_PERCENTAGE,
     RADIO_NR_RANDOM_ELEMENTS,
     &random_placement_method,          RANDOM_USE_QUANTITY,
@@ -1367,7 +1483,7 @@ static struct
     NULL, "double speed movement",     "set movement speed of player"
   },
   {
-    ED_SETTINGS_XPOS(0) + 340,         ED_COUNTER_YPOS(6) - MINI_TILEY,
+    -1,                                        ED_COUNTER_YPOS(6) - MINI_TILEY,
     GADGET_ID_GRAVITY,                 GADGET_ID_DOUBLE_SPEED,
     &level.gravity,
     " ", "gravity",                    "set level gravity"
@@ -1424,7 +1540,7 @@ static struct
     NULL, "can fall",                  "element can fall down"
   },
   {
-    ED_SETTINGS_XPOS(6),               ED_SETTINGS_YPOS(9),
+    -1,                                        ED_SETTINGS_YPOS(9),
     GADGET_ID_CUSTOM_CAN_SMASH,                GADGET_ID_CUSTOM_CAN_FALL,
     &custom_element_properties[EP_CAN_SMASH],
     " ", NULL,                         "element can smash other elements"
@@ -1454,13 +1570,13 @@ static struct
     NULL, "by fire",                   "element can explode by fire/explosion"
   },
   {
-    ED_SETTINGS_XPOS(7),               ED_SETTINGS_YPOS(13),
+    -1,                                        ED_SETTINGS_YPOS(13),
     GADGET_ID_CUSTOM_EXPLODE_SMASH,    GADGET_ID_CUSTOM_EXPLODE_FIRE,
     &custom_element.can_explode_smashed,
     " ", "smashed",                    "element can explode when smashed"
   },
   {
-    ED_SETTINGS_XPOS(13),              ED_SETTINGS_YPOS(13),
+    -1,                                        ED_SETTINGS_YPOS(13),
     GADGET_ID_CUSTOM_EXPLODE_IMPACT,   GADGET_ID_CUSTOM_EXPLODE_SMASH,
     &custom_element.can_explode_impact,
     " ", "impact",                     "element can explode on impact"
@@ -1477,7 +1593,7 @@ static struct
   {
     ED_SETTINGS_XPOS(0),               ED_SETTINGS_YPOS(2),
     GADGET_ID_CUSTOM_CAN_CHANGE,       GADGET_ID_NONE,
-    &custom_element_properties[EP_CAN_CHANGE],
+    &custom_element_change.can_change,
     NULL, "element changes to:",       "element can change to other element"
   },
   {
@@ -1488,48 +1604,42 @@ static struct
   },
   {
     ED_SETTINGS_XPOS(1),               ED_SETTINGS_YPOS(5),
-    GADGET_ID_CHANGE_BY_PLAYER,                GADGET_ID_NONE,
-    &custom_element_change_events[CE_BY_PLAYER],
-    NULL, NULL,                                "element changes by player contact"
+    GADGET_ID_CHANGE_BY_DIRECT_ACT,    GADGET_ID_NONE,
+    &custom_element_change_events[CE_BY_DIRECT_ACTION],
+    NULL, NULL,                                "element changes by direct action"
   },
   {
     ED_SETTINGS_XPOS(1),               ED_SETTINGS_YPOS(6),
-    GADGET_ID_CHANGE_BY_COLLISION,     GADGET_ID_NONE,
-    &custom_element_change_events[CE_BY_COLLISION],
-    NULL, NULL,                                "element changes by impact or smash"
-  },
-  {
-    ED_SETTINGS_XPOS(1),               ED_SETTINGS_YPOS(7),
-    GADGET_ID_CHANGE_BY_OTHER,         GADGET_ID_NONE,
-    &custom_element_change_events[CE_BY_OTHER],
+    GADGET_ID_CHANGE_BY_OTHER_ACT,     GADGET_ID_NONE,
+    &custom_element_change_events[CE_BY_OTHER_ACTION],
     NULL, NULL,                                "element changes by other element"
   },
   {
-    ED_SETTINGS_XPOS(1),               ED_SETTINGS_YPOS(8),
+    ED_SETTINGS_XPOS(1),               ED_SETTINGS_YPOS(7),
     GADGET_ID_CHANGE_USE_EXPLOSION,    GADGET_ID_NONE,
-    &custom_element.change.explode,
+    &custom_element_change.explode,
     NULL, "explode instead of change", "element explodes instead of change"
   },
   {
-    ED_SETTINGS_XPOS(1),               ED_SETTINGS_YPOS(9),
+    ED_SETTINGS_XPOS(1),               ED_SETTINGS_YPOS(8),
     GADGET_ID_CHANGE_USE_CONTENT,      GADGET_ID_NONE,
-    &custom_element.change.use_content,
+    &custom_element_change.use_content,
     NULL, "use extended change target:","element changes to more elements"
   },
   {
-    ED_SETTINGS_XPOS(2),               ED_SETTINGS_YPOS(11),
+    ED_SETTINGS_XPOS(2),               ED_SETTINGS_YPOS(10),
     GADGET_ID_CHANGE_ONLY_COMPLETE,    GADGET_ID_NONE,
-    &custom_element.change.only_complete,
+    &custom_element_change.only_complete,
     NULL, "only use complete change",  "only use complete extended content"
   },
   {
-    ED_SETTINGS_XPOS(2),               ED_SETTINGS_YPOS(12),
+    ED_SETTINGS_XPOS(2),               ED_SETTINGS_YPOS(11),
     GADGET_ID_CHANGE_USE_RANDOM,       GADGET_ID_NONE,
-    &custom_element.change.use_random_change,
+    &custom_element_change.use_random_change,
     NULL, NULL,                                "use random value for new content"
   },
   {
-    ED_SETTINGS_XPOS(0),               ED_SETTINGS_YPOS(13),
+    ED_SETTINGS_XPOS(0),               ED_SETTINGS_YPOS(12),
     GADGET_ID_CUSTOM_USE_TEMPLATE,     GADGET_ID_NONE,
     &level.use_custom_template,
     NULL, "use template",              "use template for custom properties"
@@ -1617,7 +1727,7 @@ static struct
   /* ---------- custom graphic --------------------------------------------- */
 
   {
-    ED_AREA_ELEM_CONTENT3_XPOS,                ED_AREA_ELEM_CONTENT3_YPOS,
+    -1,                                        ED_AREA_ELEM_CONTENT3_YPOS,
     1, 1,
     GADGET_ID_CUSTOM_GRAPHIC,          GADGET_ID_CUSTOM_USE_GRAPHIC,
     NULL, NULL,                                NULL
@@ -1635,7 +1745,7 @@ static struct
   /* ---------- custom change target --------------------------------------- */
 
   {
-    ED_AREA_ELEM_CONTENT2_XPOS,                ED_AREA_ELEM_CONTENT2_YPOS,
+    -1,                                        ED_AREA_ELEM_CONTENT2_YPOS,
     1, 1,
     GADGET_ID_CUSTOM_CHANGE_TARGET,    GADGET_ID_CUSTOM_CAN_CHANGE,
     NULL, "after/when:",               NULL
@@ -1653,7 +1763,7 @@ static struct
   /* ---------- custom change trigger (element causing change) ------------- */
 
   {
-    ED_AREA_ELEM_CONTENT5_XPOS,                ED_AREA_ELEM_CONTENT5_YPOS,
+    -1,                                        ED_AREA_ELEM_CONTENT5_YPOS,
     1, 1,
     GADGET_ID_CUSTOM_CHANGE_TRIGGER,   GADGET_ID_CHANGE_OTHER_ACTION,
     NULL, NULL,                                NULL
@@ -1662,7 +1772,7 @@ static struct
   /* ---------- random background (for random painting) -------------------- */
 
   {
-    ED_AREA_RANDOM_BACKGROUND_XPOS,    ED_AREA_RANDOM_BACKGROUND_YPOS,
+    -1,                                        ED_AREA_RANDOM_BACKGROUND_YPOS,
     1, 1,
     GADGET_ID_RANDOM_BACKGROUND,       GADGET_ID_RANDOM_RESTRICTED,
     NULL, NULL,                                NULL
@@ -1708,8 +1818,10 @@ static void CopyLevelToUndoBuffer(int);
 static void HandleDrawingAreas(struct GadgetInfo *);
 static void HandleCounterButtons(struct GadgetInfo *);
 static void HandleTextInputGadgets(struct GadgetInfo *);
+static void HandleTextAreaGadgets(struct GadgetInfo *);
 static void HandleSelectboxGadgets(struct GadgetInfo *);
 static void HandleTextbuttonGadgets(struct GadgetInfo *);
+static void HandleGraphicbuttonGadgets(struct GadgetInfo *);
 static void HandleRadiobuttons(struct GadgetInfo *);
 static void HandleCheckbuttons(struct GadgetInfo *);
 static void HandleControlButtons(struct GadgetInfo *);
@@ -1733,7 +1845,7 @@ static int edit_mode_properties;
 
 static int element_shift = 0;
 
-static int editor_el_boulderdash[] =
+static int editor_hl_boulderdash[] =
 {
   EL_CHAR('B'),
   EL_CHAR('O'),
@@ -1749,7 +1861,10 @@ static int editor_el_boulderdash[] =
   EL_CHAR('A'),
   EL_CHAR('S'),
   EL_CHAR('H'),
+};
 
+static int editor_el_boulderdash[] =
+{
   EL_PLAYER_1,
   EL_EMPTY,
   EL_SAND,
@@ -1775,9 +1890,10 @@ static int editor_el_boulderdash[] =
   EL_BD_FIREFLY_DOWN,
   EL_EMPTY,
 };
+static int num_editor_hl_boulderdash = SIZEOF_ARRAY_INT(editor_hl_boulderdash);
 static int num_editor_el_boulderdash = SIZEOF_ARRAY_INT(editor_el_boulderdash);
 
-static int editor_el_emerald_mine[] =
+static int editor_hl_emerald_mine[] =
 {
   EL_CHAR('E'),
   EL_CHAR('M'),
@@ -1793,7 +1909,10 @@ static int editor_el_emerald_mine[] =
   EL_CHAR('I'),
   EL_CHAR('N'),
   EL_CHAR('E'),
+};
 
+static int editor_el_emerald_mine[] =
+{
   EL_PLAYER_1,
   EL_PLAYER_2,
   EL_PLAYER_3,
@@ -1869,15 +1988,19 @@ static int editor_el_emerald_mine[] =
   EL_EM_GATE_3_GRAY,
   EL_EM_GATE_4_GRAY,
 };
-static int num_editor_el_emerald_mine = SIZEOF_ARRAY_INT(editor_el_emerald_mine);
+static int num_editor_hl_emerald_mine=SIZEOF_ARRAY_INT(editor_hl_emerald_mine);
+static int num_editor_el_emerald_mine=SIZEOF_ARRAY_INT(editor_el_emerald_mine);
 
-static int editor_el_more[] =
+static int editor_hl_more[] =
 {
   EL_CHAR('M'),
   EL_CHAR('O'),
   EL_CHAR('R'),
   EL_CHAR('E'),
+};
 
+static int editor_el_more[] =
+{
   EL_KEY_1,
   EL_KEY_2,
   EL_KEY_3,
@@ -1973,9 +2096,10 @@ static int editor_el_more[] =
   EL_EMC_WALL_6,
   EL_EMC_WALL_7,
 };
+static int num_editor_hl_more = SIZEOF_ARRAY_INT(editor_hl_more);
 static int num_editor_el_more = SIZEOF_ARRAY_INT(editor_el_more);
 
-static int editor_el_sokoban[] =
+static int editor_hl_sokoban[] =
 {
   EL_CHAR('S'),
   EL_CHAR('O'),
@@ -1986,15 +2110,19 @@ static int editor_el_sokoban[] =
   EL_CHAR('B'),
   EL_CHAR('A'),
   EL_CHAR('N'),
+};
 
+static int editor_el_sokoban[] =
+{
   EL_SOKOBAN_OBJECT,
   EL_SOKOBAN_FIELD_EMPTY,
   EL_SOKOBAN_FIELD_FULL,
   EL_STEELWALL,
 };
+static int num_editor_hl_sokoban = SIZEOF_ARRAY_INT(editor_hl_sokoban);
 static int num_editor_el_sokoban = SIZEOF_ARRAY_INT(editor_el_sokoban);
 
-static int editor_el_supaplex[] =
+static int editor_hl_supaplex[] =
 {
   EL_CHAR('S'),
   EL_CHAR('U'),
@@ -2005,7 +2133,10 @@ static int editor_el_supaplex[] =
   EL_CHAR('L'),
   EL_CHAR('E'),
   EL_CHAR('X'),
+};
 
+static int editor_el_supaplex[] =
+{
   EL_SP_EMPTY,
   EL_SP_ZONK,
   EL_SP_BASE,
@@ -2056,9 +2187,10 @@ static int editor_el_supaplex[] =
   EL_SP_CHIP_TOP,
   EL_SP_CHIP_BOTTOM,
 };
+static int num_editor_hl_supaplex = SIZEOF_ARRAY_INT(editor_hl_supaplex);
 static int num_editor_el_supaplex = SIZEOF_ARRAY_INT(editor_el_supaplex);
 
-static int editor_el_diamond_caves[] =
+static int editor_hl_diamond_caves[] =
 {
   EL_CHAR('D'),
   EL_CHAR('I'),
@@ -2079,7 +2211,10 @@ static int editor_el_diamond_caves[] =
   EL_CHAR(' '),
   EL_CHAR('I'),
   EL_CHAR('I'),
+};
 
+static int editor_el_diamond_caves[] =
+{
   EL_PEARL,
   EL_CRYSTAL,
   EL_WALL_PEARL,
@@ -2140,9 +2275,10 @@ static int editor_el_diamond_caves[] =
   EL_EXTRA_TIME,
   EL_EMPTY,
 };
+static int num_editor_hl_diamond_caves = SIZEOF_ARRAY_INT(editor_hl_diamond_caves);
 static int num_editor_el_diamond_caves = SIZEOF_ARRAY_INT(editor_el_diamond_caves);
 
-static int editor_el_dx_boulderdash[] =
+static int editor_hl_dx_boulderdash[] =
 {
   EL_CHAR('D'),
   EL_CHAR('X'),
@@ -2163,7 +2299,10 @@ static int editor_el_dx_boulderdash[] =
   EL_CHAR('A'),
   EL_CHAR('S'),
   EL_CHAR('H'),
+};
 
+static int editor_el_dx_boulderdash[] =
+{
   EL_SPRING,
   EL_TUBE_RIGHT_DOWN,
   EL_TUBE_HORIZONTAL_DOWN,
@@ -2184,15 +2323,19 @@ static int editor_el_dx_boulderdash[] =
   EL_EMPTY,
   EL_EMPTY
 };
+static int num_editor_hl_dx_boulderdash = SIZEOF_ARRAY_INT(editor_hl_dx_boulderdash);
 static int num_editor_el_dx_boulderdash = SIZEOF_ARRAY_INT(editor_el_dx_boulderdash);
 
-static int editor_el_chars[] =
+static int editor_hl_chars[] =
 {
   EL_CHAR('T'),
   EL_CHAR('E'),
   EL_CHAR('X'),
   EL_CHAR('T'),
+};
 
+static int editor_el_chars[] =
+{
   EL_CHAR(' '),
   EL_CHAR('!'),
   EL_CHAR('"'),
@@ -2283,9 +2426,10 @@ static int editor_el_chars[] =
   EL_CHAR(FONT_ASCII_CURSOR),
   EL_CHAR(' ')
 };
+static int num_editor_hl_chars = SIZEOF_ARRAY_INT(editor_hl_chars);
 static int num_editor_el_chars = SIZEOF_ARRAY_INT(editor_el_chars);
 
-static int editor_el_custom[] =
+static int editor_hl_custom[] =
 {
   EL_CHAR('C'),
   EL_CHAR('U'),
@@ -2306,7 +2450,10 @@ static int editor_el_custom[] =
   EL_CHAR('N'),
   EL_CHAR('T'),
   EL_CHAR('S'),
+};
 
+static int editor_el_custom[] =
+{
   EL_CUSTOM_START + 0,
   EL_CUSTOM_START + 1,
   EL_CUSTOM_START + 2,
@@ -2467,8 +2614,13 @@ static int editor_el_custom[] =
   EL_CUSTOM_START + 126,
   EL_CUSTOM_START + 127
 };
+static int num_editor_hl_custom = SIZEOF_ARRAY_INT(editor_hl_custom);
 static int num_editor_el_custom = SIZEOF_ARRAY_INT(editor_el_custom);
 
+static int editor_hl_custom_more[] =
+{
+};
+
 static int editor_el_custom_more[] =
 {
   EL_CUSTOM_START + 128,
@@ -2631,6 +2783,7 @@ static int editor_el_custom_more[] =
   EL_CUSTOM_START + 254,
   EL_CUSTOM_START + 255
 };
+static int num_editor_hl_custom_more = SIZEOF_ARRAY_INT(editor_hl_custom_more);
 static int num_editor_el_custom_more = SIZEOF_ARRAY_INT(editor_el_custom_more);
 
 static int *editor_elements = NULL;    /* dynamically allocated */
@@ -2639,6 +2792,10 @@ static int num_editor_elements = 0;      /* dynamically determined */
 static struct
 {
   boolean *setup_value;
+
+  int *headline_list;
+  int *headline_list_size;
+
   int *element_list;
   int *element_list_size;
 
@@ -2646,28 +2803,61 @@ static struct
 }
 editor_elements_info[] =
 {
-  { &setup.editor.el_boulderdash,      editor_el_boulderdash,
-    &num_editor_el_boulderdash                                         },
-  { &setup.editor.el_emerald_mine,     editor_el_emerald_mine,
-    &num_editor_el_emerald_mine                                                },
-  { &setup.editor.el_more,             editor_el_more,
-    &num_editor_el_more                                                        },
-  { &setup.editor.el_sokoban,          editor_el_sokoban,
-    &num_editor_el_sokoban                                             },
-  { &setup.editor.el_supaplex,         editor_el_supaplex,
-    &num_editor_el_supaplex                                            },
-  { &setup.editor.el_diamond_caves,    editor_el_diamond_caves,
-    &num_editor_el_diamond_caves                                       },
-  { &setup.editor.el_dx_boulderdash,   editor_el_dx_boulderdash,
-    &num_editor_el_dx_boulderdash                                      },
-  { &setup.editor.el_chars,            editor_el_chars,
-    &num_editor_el_chars                                               },
-  { &setup.editor.el_custom,           editor_el_custom,
-    &num_editor_el_custom                                              },
-  { &setup.editor.el_custom_more,      editor_el_custom_more,
-    &num_editor_el_custom_more                                         },
-  { NULL,                              NULL,
-    NULL                                                               }
+  {
+    &setup.editor.el_boulderdash,
+    editor_hl_boulderdash,             &num_editor_hl_boulderdash,
+    editor_el_boulderdash,             &num_editor_el_boulderdash
+  },
+  {
+    &setup.editor.el_emerald_mine,
+    editor_hl_emerald_mine,            &num_editor_hl_emerald_mine,
+    editor_el_emerald_mine,            &num_editor_el_emerald_mine
+  },
+  {
+    &setup.editor.el_more,
+    editor_hl_more,                    &num_editor_hl_more,
+    editor_el_more,                    &num_editor_el_more
+  },
+  {
+    &setup.editor.el_sokoban,
+    editor_hl_sokoban,                 &num_editor_hl_sokoban,
+    editor_el_sokoban,                 &num_editor_el_sokoban
+  },
+  {
+    &setup.editor.el_supaplex,
+    editor_hl_supaplex,                        &num_editor_hl_supaplex,
+    editor_el_supaplex,                        &num_editor_el_supaplex
+  },
+  {
+    &setup.editor.el_diamond_caves,
+    editor_hl_diamond_caves,           &num_editor_hl_diamond_caves,
+    editor_el_diamond_caves,           &num_editor_el_diamond_caves
+  },
+  {
+    &setup.editor.el_dx_boulderdash,
+    editor_hl_dx_boulderdash,          &num_editor_hl_dx_boulderdash,
+    editor_el_dx_boulderdash,          &num_editor_el_dx_boulderdash
+  },
+  {
+    &setup.editor.el_chars,
+    editor_hl_chars,                   &num_editor_hl_chars,
+    editor_el_chars,                   &num_editor_el_chars
+  },
+  {
+    &setup.editor.el_custom,
+    editor_hl_custom,                  &num_editor_hl_custom,
+    editor_el_custom,                  &num_editor_el_custom
+  },
+  {
+    &setup.editor.el_custom_more,
+    editor_hl_custom_more,             &num_editor_hl_custom_more,
+    editor_el_custom_more,             &num_editor_el_custom_more
+  },
+  {
+    NULL,
+    NULL,                              NULL,
+    NULL,                              NULL
+  }
 };
 
 
@@ -2689,8 +2879,15 @@ static void ReinitializeElementList()
 
   /* determine size of element list */
   for (i=0; editor_elements_info[i].setup_value != NULL; i++)
+  {
     if (*editor_elements_info[i].setup_value)
+    {
+      if (setup.editor.el_headlines)
+       num_editor_elements += *editor_elements_info[i].headline_list_size;
+
       num_editor_elements += *editor_elements_info[i].element_list_size;
+    }
+  }
 
   if (num_editor_elements < ED_NUM_ELEMENTLIST_BUTTONS)
   {
@@ -2705,9 +2902,17 @@ static void ReinitializeElementList()
 
   /* fill element list */
   for (i=0; editor_elements_info[i].setup_value != NULL; i++)
+  {
     if (*editor_elements_info[i].setup_value)
-      for (j=0; j<*editor_elements_info[i].element_list_size; j++)
+    {
+      if (setup.editor.el_headlines)
+       for (j=0; j < *editor_elements_info[i].headline_list_size; j++)
+         editor_elements[pos++] = editor_elements_info[i].headline_list[j];
+
+      for (j=0; j < *editor_elements_info[i].element_list_size; j++)
        editor_elements[pos++] = editor_elements_info[i].element_list[j];
+    }
+  }
 
   /* correct position of element list scrollbar */
   if (element_shift < 0)
@@ -2718,14 +2923,20 @@ static void ReinitializeElementList()
 
 static void ReinitializeElementListButtons()
 {
+  static boolean last_setup_value_headlines = FALSE;
   static boolean initialization_needed = TRUE;
   int i;
 
   if (!initialization_needed)  /* check if editor element setup has changed */
+  {
+    if (last_setup_value_headlines != setup.editor.el_headlines)
+      initialization_needed = TRUE;
+
     for (i=0; editor_elements_info[i].setup_value != NULL; i++)
       if (editor_elements_info[i].last_setup_value !=
          *editor_elements_info[i].setup_value)
        initialization_needed = TRUE;
+  }
 
   if (!initialization_needed)
     return;
@@ -2734,6 +2945,7 @@ static void ReinitializeElementListButtons()
   CreateLevelEditorGadgets();
 
   /* store current setup values for next invocation of this function */
+  last_setup_value_headlines = setup.editor.el_headlines;
   for (i=0; editor_elements_info[i].setup_value != NULL; i++)
     editor_elements_info[i].last_setup_value =
       *editor_elements_info[i].setup_value;
@@ -2834,16 +3046,16 @@ static void DrawDrawingArea(int id)
                           el2edimg(custom_element.content[x][y]));
   else if (id == ED_DRAWING_ID_CUSTOM_CHANGE_TARGET)
     DrawMiniGraphicExt(drawto, gi->x, gi->y,
-                      el2edimg(custom_element.change.target_element));
+                      el2edimg(custom_element_change.target_element));
   else if (id == ED_DRAWING_ID_CUSTOM_CHANGE_CONTENT)
     for (y=0; y < 3; y++)
       for (x=0; x < 3; x++)
        DrawMiniGraphicExt(drawto,
                           gi->x + x * MINI_TILEX, gi->y + y * MINI_TILEY,
-                          el2edimg(custom_element.change.content[x][y]));
+                          el2edimg(custom_element_change.content[x][y]));
   else if (id == ED_DRAWING_ID_CUSTOM_CHANGE_TRIGGER)
     DrawMiniGraphicExt(drawto, gi->x, gi->y,
-                      el2edimg(custom_element.change.trigger_element));
+                      el2edimg(custom_element_change.trigger_element));
   else if (id >= ED_DRAWING_ID_ELEMENT_CONTENT_0 &&
           id <= ED_DRAWING_ID_ELEMENT_CONTENT_7)
   {
@@ -3221,7 +3433,7 @@ static void CreateCounterButtons()
                          GDI_INFO_TEXT, "enter counter value",
                          GDI_X, x,
                          GDI_Y, y,
-                         GDI_TYPE, GD_TYPE_TEXTINPUT_NUMERIC,
+                         GDI_TYPE, GD_TYPE_TEXT_INPUT_NUMERIC,
                          GDI_NUMBER_VALUE, 0,
                          GDI_NUMBER_MIN, counterbutton_info[i].min_value,
                          GDI_NUMBER_MAX, counterbutton_info[i].max_value,
@@ -3325,7 +3537,7 @@ static void CreateTextInputGadgets()
                      GDI_INFO_TEXT, infotext,
                      GDI_X, SX + textinput_info[i].x,
                      GDI_Y, SY + textinput_info[i].y,
-                     GDI_TYPE, GD_TYPE_TEXTINPUT_ALPHANUMERIC,
+                     GDI_TYPE, GD_TYPE_TEXT_INPUT_ALPHANUMERIC,
                      GDI_TEXT_VALUE, textinput_info[i].value,
                      GDI_TEXT_SIZE, textinput_info[i].size,
                      GDI_TEXT_FONT, FONT_INPUT_1,
@@ -3346,6 +3558,55 @@ static void CreateTextInputGadgets()
   }
 }
 
+static void CreateTextAreaGadgets()
+{
+  int max_infotext_len = getMaxInfoTextLength();
+  int i;
+
+  for (i=0; i<ED_NUM_TEXTAREAS; i++)
+  {
+    Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
+    int gd_x, gd_y;
+    struct GadgetInfo *gi;
+    unsigned long event_mask;
+    char infotext[MAX_OUTPUT_LINESIZE + 1];
+    int id = textarea_info[i].gadget_id;
+    int area_xsize = textarea_info[i].xsize;
+    int area_ysize = textarea_info[i].ysize;
+
+    event_mask = GD_EVENT_TEXT_LEAVING;
+
+    gd_x = DOOR_GFX_PAGEX4 + ED_WIN_COUNT_XPOS;
+    gd_y = DOOR_GFX_PAGEY1 + ED_WIN_COUNT_YPOS;
+
+    sprintf(infotext, "Enter %s", textarea_info[i].infotext);
+    infotext[max_infotext_len] = '\0';
+
+    gi = CreateGadget(GDI_CUSTOM_ID, id,
+                     GDI_CUSTOM_TYPE_ID, i,
+                     GDI_INFO_TEXT, infotext,
+                     GDI_X, SX + textarea_info[i].x,
+                     GDI_Y, SY + textarea_info[i].y,
+                     GDI_TYPE, GD_TYPE_TEXT_AREA,
+                     GDI_AREA_SIZE, area_xsize, area_ysize,
+                     GDI_TEXT_FONT, FONT_INPUT_1,
+                     GDI_TEXT_FONT_ACTIVE, FONT_INPUT_1_ACTIVE,
+                     GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x, gd_y,
+                     GDI_DESIGN_PRESSED, gd_bitmap, gd_x, gd_y,
+                     GDI_BORDER_SIZE, ED_BORDER_SIZE, ED_BORDER_SIZE,
+                     GDI_DESIGN_WIDTH, ED_WIN_COUNT_XSIZE,
+                     GDI_EVENT_MASK, event_mask,
+                     GDI_CALLBACK_INFO, HandleEditorGadgetInfoText,
+                     GDI_CALLBACK_ACTION, HandleTextAreaGadgets,
+                     GDI_END);
+
+    if (gi == NULL)
+      Error(ERR_EXIT, "cannot create gadget");
+
+    level_editor_gadget[id] = gi;
+  }
+}
+
 static void CreateSelectboxGadgets()
 {
   int max_infotext_len = getMaxInfoTextLength();
@@ -3428,7 +3689,7 @@ static void CreateTextbuttonGadgets()
   int max_infotext_len = getMaxInfoTextLength();
   int i;
 
-  for (i=0; i<ED_NUM_TEXTBUTTON; i++)
+  for (i=0; i<ED_NUM_TEXTBUTTONS; i++)
   {
     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
     int gd_x1, gd_x2, gd_y1, gd_y2;
@@ -3467,6 +3728,10 @@ static void CreateTextbuttonGadgets()
       x = (right_gadget_border[textbutton_info[i].gadget_id_align] +
           ED_GADGET_TEXT_DISTANCE);
 
+    /* determine horizontal offset for leading text */
+    if (textbutton_info[i].text_left != NULL)
+      x += getTextWidthForGadget(textbutton_info[i].text_left);
+
     gi = CreateGadget(GDI_CUSTOM_ID, id,
                      GDI_CUSTOM_TYPE_ID, i,
                      GDI_INFO_TEXT, infotext,
@@ -3492,6 +3757,64 @@ static void CreateTextbuttonGadgets()
       Error(ERR_EXIT, "cannot create gadget");
 
     level_editor_gadget[id] = gi;
+    right_gadget_border[id] =
+      getRightGadgetBorder(gi, textbutton_info[i].text_right);
+  }
+}
+
+static void CreateGraphicbuttonGadgets()
+{
+  Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
+  struct GadgetInfo *gi;
+  unsigned long event_mask;
+  int i;
+
+  /* create buttons for scrolling of drawing area and element list */
+  for (i=0; i < ED_NUM_GRAPHICBUTTONS; i++)
+  {
+    int id = graphicbutton_info[i].gadget_id;
+    int gd_x1, gd_x2, gd_y1, gd_y2;
+    int x = SX + graphicbutton_info[i].x;
+    int y = SY + graphicbutton_info[i].y;
+
+    event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
+
+    gd_x1 = DOOR_GFX_PAGEX4 + graphicbutton_info[i].gd_x;
+    gd_x2 = DOOR_GFX_PAGEX3 + graphicbutton_info[i].gd_x;
+    gd_y1 = DOOR_GFX_PAGEY1 + graphicbutton_info[i].gd_y;
+    gd_y2 = gd_y1;
+
+    /* determine horizontal position to the right of specified gadget */
+    if (graphicbutton_info[i].gadget_id_align != GADGET_ID_NONE)
+      x = (right_gadget_border[graphicbutton_info[i].gadget_id_align] +
+          ED_GADGET_TEXT_DISTANCE);
+
+    /* determine horizontal offset for leading text */
+    if (graphicbutton_info[i].text_left != NULL)
+      x += getTextWidthForGadget(graphicbutton_info[i].text_left);
+
+    gi = CreateGadget(GDI_CUSTOM_ID, id,
+                     GDI_CUSTOM_TYPE_ID, i,
+                     GDI_INFO_TEXT, graphicbutton_info[i].infotext,
+                     GDI_X, x,
+                     GDI_Y, y,
+                     GDI_WIDTH, graphicbutton_info[i].width,
+                     GDI_HEIGHT, graphicbutton_info[i].height,
+                     GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
+                     GDI_STATE, GD_BUTTON_UNPRESSED,
+                     GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
+                     GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
+                     GDI_EVENT_MASK, event_mask,
+                     GDI_CALLBACK_INFO, HandleEditorGadgetInfoText,
+                     GDI_CALLBACK_ACTION, HandleGraphicbuttonGadgets,
+                     GDI_END);
+
+    if (gi == NULL)
+      Error(ERR_EXIT, "cannot create gadget");
+
+    level_editor_gadget[id] = gi;
+    right_gadget_border[id] =
+      getRightGadgetBorder(gi, graphicbutton_info[i].text_right);
   }
 }
 
@@ -3707,8 +4030,10 @@ void CreateLevelEditorGadgets()
   CreateCounterButtons();
   CreateRadiobuttonGadgets();
   CreateTextInputGadgets();
-  CreateTextbuttonGadgets();
+  CreateTextAreaGadgets();
   CreateSelectboxGadgets();
+  CreateGraphicbuttonGadgets();
+  CreateTextbuttonGadgets();
   CreateDrawingAreas();
 
   game_status = old_game_status;
@@ -3823,25 +4148,35 @@ static void MapDrawingArea(int id)
 static void MapTextInputGadget(int id)
 {
   struct GadgetInfo *gi = level_editor_gadget[textinput_info[id].gadget_id];
-  char infotext[MAX_OUTPUT_LINESIZE + 1];
-  int max_infotext_len = getMaxInfoTextLength();
   int xoffset_above = 0;
   int yoffset_above = -(MINI_TILEX + ED_GADGET_DISTANCE);
-  int x = textinput_info[id].x + xoffset_above;
-  int y = textinput_info[id].y + yoffset_above;
+  int x_above = textinput_info[id].x + xoffset_above;
+  int y_above = textinput_info[id].y + yoffset_above;
 
-  if (textinput_info[id].infotext)
-  {
-    sprintf(infotext, "%s:", textinput_info[id].infotext);
-    infotext[max_infotext_len] = '\0';
-    DrawTextF(x, y, FONT_TEXT_1, infotext);
-  }
+  if (textinput_info[id].text_above)
+    DrawTextF(x_above, y_above, FONT_TEXT_1, textinput_info[id].text_above);
 
   ModifyGadget(gi, GDI_TEXT_VALUE, textinput_info[id].value, GDI_END);
 
   MapGadget(gi);
 }
 
+static void MapTextAreaGadget(int id)
+{
+  struct GadgetInfo *gi = level_editor_gadget[textarea_info[id].gadget_id];
+  int xoffset_above = 0;
+  int yoffset_above = -(MINI_TILEX + ED_GADGET_DISTANCE);
+  int x_above = textarea_info[id].x + xoffset_above;
+  int y_above = textarea_info[id].y + yoffset_above;
+
+  if (textarea_info[id].text_above)
+    DrawTextF(x_above, y_above, FONT_TEXT_1, textarea_info[id].text_above);
+
+  ModifyGadget(gi, GDI_TEXT_VALUE, textarea_info[id].value, GDI_END);
+
+  MapGadget(gi);
+}
+
 static void MapSelectboxGadget(int id)
 {
   struct GadgetInfo *gi = level_editor_gadget[selectbox_info[id].gadget_id];
@@ -3866,6 +4201,42 @@ static void MapSelectboxGadget(int id)
 static void MapTextbuttonGadget(int id)
 {
   struct GadgetInfo *gi = level_editor_gadget[textbutton_info[id].gadget_id];
+  int xoffset_left = getTextWidthForGadget(textbutton_info[id].text_left);
+  int xoffset_right = ED_GADGET_TEXT_DISTANCE;
+  int yoffset = ED_BORDER_SIZE;
+  int x_left = gi->x - xoffset_left;
+  int x_right = gi->x + gi->width + xoffset_right;
+  int y = gi->y + yoffset;
+
+  /* only show button to delete change pages when more than minimum pages */
+  if (id == ED_TEXTBUTTON_ID_DEL_CHANGE_PAGE &&
+      custom_element.num_change_pages == MIN_CHANGE_PAGES)
+    return;
+
+  if (textbutton_info[id].text_left)
+    DrawText(x_left, y, textbutton_info[id].text_left, FONT_TEXT_1);
+
+  if (textbutton_info[id].text_right)
+    DrawText(x_right, y, textbutton_info[id].text_right, FONT_TEXT_1);
+
+  MapGadget(gi);
+}
+
+static void MapGraphicbuttonGadget(int id)
+{
+  struct GadgetInfo *gi= level_editor_gadget[graphicbutton_info[id].gadget_id];
+  int xoffset_left = getTextWidthForGadget(graphicbutton_info[id].text_left);
+  int xoffset_right = ED_GADGET_TEXT_DISTANCE;
+  int yoffset = ED_BORDER_SIZE;
+  int x_left = gi->x - xoffset_left;
+  int x_right = gi->x + gi->width + xoffset_right;
+  int y = gi->y + yoffset;
+
+  if (graphicbutton_info[id].text_left)
+    DrawText(x_left, y, graphicbutton_info[id].text_left, FONT_TEXT_1);
+
+  if (graphicbutton_info[id].text_right)
+    DrawText(x_right, y, graphicbutton_info[id].text_right, FONT_TEXT_1);
 
   MapGadget(gi);
 }
@@ -3873,19 +4244,12 @@ static void MapTextbuttonGadget(int id)
 static void MapRadiobuttonGadget(int id)
 {
   struct GadgetInfo *gi = level_editor_gadget[radiobutton_info[id].gadget_id];
-#if 0
-  int xoffset_right = ED_XOFFSET_CHECKBOX;
-  int yoffset_right = ED_BORDER_SIZE;
-  int x = radiobutton_info[id].x + xoffset_right;
-  int y = radiobutton_info[id].y + yoffset_right;
-#else
   int xoffset_left = getTextWidthForGadget(checkbutton_info[id].text_left);
   int xoffset_right = ED_GADGET_TEXT_DISTANCE;
   int yoffset = ED_BORDER_SIZE;
   int x_left = gi->x - xoffset_left;
   int x_right = gi->x + gi->width + xoffset_right;
   int y = gi->y + yoffset;
-#endif
   boolean checked =
     (*radiobutton_info[id].value == radiobutton_info[id].checked_value);
 
@@ -3903,19 +4267,12 @@ static void MapRadiobuttonGadget(int id)
 static void MapCheckbuttonGadget(int id)
 {
   struct GadgetInfo *gi = level_editor_gadget[checkbutton_info[id].gadget_id];
-#if 0
-  int xoffset_right = ED_XOFFSET_CHECKBOX;
-  int yoffset_right = ED_BORDER_SIZE;
-  int x = checkbutton_info[id].x + xoffset_right;
-  int y = checkbutton_info[id].y + yoffset_right;
-#else
   int xoffset_left = getTextWidthForGadget(checkbutton_info[id].text_left);
   int xoffset_right = ED_GADGET_TEXT_DISTANCE;
   int yoffset = ED_BORDER_SIZE;
   int x_left = gi->x - xoffset_left;
   int x_right = gi->x + gi->width + xoffset_right;
   int y = gi->y + yoffset;
-#endif
 
   /* special case needed for "sticky" gadget */
   ModifyGadget(gi, GDI_CHECKED, *checkbutton_info[id].value,
@@ -4041,7 +4398,7 @@ static void CopyPlayfield(short src[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
       dst[x][y] = src[x][y];
 }
 
-static int SetSelectboxValue(int selectbox_id, int new_value)
+static int setSelectboxValue(int selectbox_id, int new_value)
 {
   int new_index_value = 0;
   int i;
@@ -4059,15 +4416,32 @@ static int SetSelectboxValue(int selectbox_id, int new_value)
 static void CopyCustomElementPropertiesToEditor(int element)
 {
   int i;
+  int current_change_page = element_info[element].current_change_page;
+
+  /* dynamically (re)build selectbox for selecting change page */
+  for (i=0; i < element_info[element].num_change_pages; i++)
+  {
+    sprintf(options_change_page_strings[i], "%d", i + 1);
+
+    options_change_page[i].value = i;
+    options_change_page[i].text = options_change_page_strings[i];
+  }
+
+  options_change_page[i].value = -1;
+  options_change_page[i].text = NULL;
 
   /* needed here to initialize combined element properties */
   InitElementPropertiesEngine(level.game_version);
 
+  element_info[element].change =
+    &element_info[element].change_page[current_change_page];
+
   custom_element = element_info[element];
+  custom_element_change = *element_info[element].change;
 
   /* needed to initially set selectbox value variables to reliable defaults */
   for (i=0; i < ED_NUM_SELECTBOX; i++)
-    SetSelectboxValue(i, *selectbox_info[i].value);
+    setSelectboxValue(i, *selectbox_info[i].value);
 
   for (i=0; i < NUM_ELEMENT_PROPERTIES; i++)
     custom_element_properties[i] = HAS_PROPERTY(element, i);
@@ -4148,32 +4522,30 @@ static void CopyCustomElementPropertiesToEditor(int element)
 
   /* ---------- element settings: advanced (custom elements) --------------- */
 
-  /* set change by player selectbox help value */
-  custom_element.change_player_action =
-    (HAS_CHANGE_EVENT(element, CE_DROPPED_BY_PLAYER) ? CE_DROPPED_BY_PLAYER :
-     HAS_CHANGE_EVENT(element, CE_PUSHED_BY_PLAYER) ? CE_PUSHED_BY_PLAYER :
+  /* set "change by direct action" selectbox help value */
+  custom_element_change.direct_action =
+    (HAS_CHANGE_EVENT(element, CE_TOUCHED_BY_PLAYER) ? CE_TOUCHED_BY_PLAYER :
      HAS_CHANGE_EVENT(element, CE_PRESSED_BY_PLAYER) ? CE_PRESSED_BY_PLAYER :
-     HAS_CHANGE_EVENT(element, CE_TOUCHED_BY_PLAYER) ? CE_TOUCHED_BY_PLAYER :
-     custom_element.change_player_action);
-
-  /* set change by collision selectbox help value */
-  custom_element.change_collide_action =
-    (HAS_CHANGE_EVENT(element, CE_SMASHED) ? CE_SMASHED :
-     HAS_CHANGE_EVENT(element, CE_IMPACT) ? CE_IMPACT :
+     HAS_CHANGE_EVENT(element, CE_PUSHED_BY_PLAYER) ? CE_PUSHED_BY_PLAYER :
+     HAS_CHANGE_EVENT(element, CE_DROPPED_BY_PLAYER) ? CE_DROPPED_BY_PLAYER :
      HAS_CHANGE_EVENT(element, CE_COLLISION) ? CE_COLLISION :
-     custom_element.change_collide_action);
+     HAS_CHANGE_EVENT(element, CE_IMPACT) ? CE_IMPACT :
+     HAS_CHANGE_EVENT(element, CE_SMASHED) ? CE_SMASHED :
+     custom_element_change.direct_action);
 
-  /* set change by other element action selectbox help value */
-  custom_element.change_other_action =
-    (HAS_CHANGE_EVENT(element, CE_OTHER_GETS_DROPPED) ? CE_OTHER_GETS_DROPPED :
-     HAS_CHANGE_EVENT(element, CE_OTHER_GETS_COLLECTED) ? CE_OTHER_GETS_COLLECTED :
-     HAS_CHANGE_EVENT(element, CE_OTHER_GETS_PUSHED) ? CE_OTHER_GETS_PUSHED :
-     HAS_CHANGE_EVENT(element, CE_OTHER_GETS_PRESSED) ? CE_OTHER_GETS_PRESSED :
+  /* set "change by other element action" selectbox help value */
+  custom_element_change.other_action =
+    (
      HAS_CHANGE_EVENT(element, CE_OTHER_GETS_TOUCHED) ? CE_OTHER_GETS_TOUCHED :
-     HAS_CHANGE_EVENT(element, CE_OTHER_IS_EXPLODING) ? CE_OTHER_IS_EXPLODING :
-     HAS_CHANGE_EVENT(element, CE_OTHER_IS_CHANGING) ? CE_OTHER_IS_CHANGING :
+     HAS_CHANGE_EVENT(element, CE_OTHER_GETS_PRESSED) ? CE_OTHER_GETS_PRESSED :
+     HAS_CHANGE_EVENT(element, CE_OTHER_GETS_PUSHED) ? CE_OTHER_GETS_PUSHED :
+     HAS_CHANGE_EVENT(element, CE_OTHER_GETS_DIGGED) ? CE_OTHER_GETS_DIGGED :
+     HAS_CHANGE_EVENT(element, CE_OTHER_GETS_COLLECTED) ? CE_OTHER_GETS_COLLECTED :
+     HAS_CHANGE_EVENT(element, CE_OTHER_GETS_DROPPED) ? CE_OTHER_GETS_DROPPED :
      HAS_CHANGE_EVENT(element, CE_OTHER_IS_TOUCHING) ? CE_OTHER_IS_TOUCHING :
-     custom_element.change_other_action);
+     HAS_CHANGE_EVENT(element, CE_OTHER_IS_CHANGING) ? CE_OTHER_IS_CHANGING :
+     HAS_CHANGE_EVENT(element, CE_OTHER_IS_EXPLODING) ? CE_OTHER_IS_EXPLODING :
+     custom_element_change.other_action);
 }
 
 static void CopyCustomElementPropertiesToGame(int element)
@@ -4201,6 +4573,7 @@ static void CopyCustomElementPropertiesToGame(int element)
   }
 
   element_info[element] = custom_element;
+  *element_info[element].change = custom_element_change;
 
   /* ---------- element settings: configure (custom elements) ------------- */
 
@@ -4268,15 +4641,11 @@ static void CopyCustomElementPropertiesToGame(int element)
   custom_element_change_events[CE_PRESSED_BY_PLAYER] = FALSE;
   custom_element_change_events[CE_PUSHED_BY_PLAYER] = FALSE;
   custom_element_change_events[CE_DROPPED_BY_PLAYER] = FALSE;
-  custom_element_change_events[custom_element.change_player_action] =
-    custom_element_change_events[CE_BY_PLAYER];
-
-  /* set collision change event from checkbox and selectbox */
   custom_element_change_events[CE_COLLISION] = FALSE;
   custom_element_change_events[CE_IMPACT] = FALSE;
   custom_element_change_events[CE_SMASHED] = FALSE;
-  custom_element_change_events[custom_element.change_collide_action] =
-    custom_element_change_events[CE_BY_COLLISION];
+  custom_element_change_events[custom_element_change.direct_action] =
+    custom_element_change_events[CE_BY_DIRECT_ACTION];
 
   /* set other element action change event from checkbox and selectbox */
   custom_element_change_events[CE_OTHER_IS_TOUCHING] = FALSE;
@@ -4285,10 +4654,11 @@ static void CopyCustomElementPropertiesToGame(int element)
   custom_element_change_events[CE_OTHER_GETS_TOUCHED] = FALSE;
   custom_element_change_events[CE_OTHER_GETS_PRESSED] = FALSE;
   custom_element_change_events[CE_OTHER_GETS_PUSHED] = FALSE;
+  custom_element_change_events[CE_OTHER_GETS_DIGGED] = FALSE;
   custom_element_change_events[CE_OTHER_GETS_COLLECTED] = FALSE;
   custom_element_change_events[CE_OTHER_GETS_DROPPED] = FALSE;
-  custom_element_change_events[custom_element.change_other_action] =
-    custom_element_change_events[CE_BY_OTHER];
+  custom_element_change_events[custom_element_change.other_action] =
+    custom_element_change_events[CE_BY_OTHER_ACTION];
 
   for (i=0; i < NUM_ELEMENT_PROPERTIES; i++)
     SET_PROPERTY(element, i, custom_element_properties[i]);
@@ -4298,6 +4668,7 @@ 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;
 }
 
 void DrawLevelEd()
@@ -4471,7 +4842,7 @@ static void ModifyEditorCounter(int counter_id, int new_value)
   ModifyGadget(gi, GDI_NUMBER_VALUE, new_value, GDI_END);
 
   if (counter_value != NULL)
-    *counter_value = gi->text.number_value;
+    *counter_value = gi->textinput.number_value;
 }
 
 static void ModifyEditorCounterLimits(int counter_id, int min, int max)
@@ -4486,7 +4857,7 @@ static void ModifyEditorSelectbox(int selectbox_id, int new_value)
 {
   int gadget_id = selectbox_info[selectbox_id].gadget_id;
   struct GadgetInfo *gi = level_editor_gadget[gadget_id];
-  int new_index_value = SetSelectboxValue(selectbox_id, new_value);
+  int new_index_value = setSelectboxValue(selectbox_id, new_value);
 
   ModifyGadget(gi, GDI_SELECTBOX_INDEX, new_index_value, GDI_END);
 }
@@ -4662,6 +5033,19 @@ static void DrawElementContentAreas()
   DrawText(x, y + 2 * MINI_TILEY, "smashed", FONT_TEXT_1);
 }
 
+static void DrawEnvelopeTextArea()
+{
+  int id = ED_TEXTAREA_ID_ENVELOPE_INFO;
+  struct GadgetInfo *gi = level_editor_gadget[textarea_info[id].gadget_id];
+
+  UnmapGadget(gi);
+  DrawBackground(gi->x, gi->y, gi->width, gi->height);
+
+  ModifyGadget(gi, GDI_AREA_SIZE, level.envelope_xsize, level.envelope_ysize,
+              GDI_END);
+  MapTextAreaGadget(ED_TEXTAREA_ID_ENVELOPE_INFO);
+}
+
 char *getElementDescriptionFilename(int element)
 {
   char *docs_dir = options.docs_directory;
@@ -5069,7 +5453,8 @@ static boolean checkPropertiesConfig()
 
   if (IS_GEM(properties_element) ||
       IS_CUSTOM_ELEMENT(properties_element) ||
-      HAS_CONTENT(properties_element))
+      HAS_CONTENT(properties_element) ||
+      properties_element == EL_ENVELOPE)
     return TRUE;
   else
     for (i=0; elements_with_counter[i].element != -1; i++)
@@ -5121,6 +5506,15 @@ static void DrawPropertiesConfig()
   if (IS_GEM(properties_element))
     MapCheckbuttonGadget(ED_CHECKBUTTON_ID_EM_SLIPPERY_GEMS);
 
+  if (properties_element == EL_ENVELOPE)
+  {
+    /* display counter to choose size of envelope text area */
+    MapCounterButtons(ED_COUNTER_ID_ENVELOPE_XSIZE);
+    MapCounterButtons(ED_COUNTER_ID_ENVELOPE_YSIZE);
+
+    DrawEnvelopeTextArea();
+  }
+
   if (IS_CUSTOM_ELEMENT(properties_element))
   {
     /* draw stickybutton gadget */
@@ -5183,7 +5577,13 @@ static void DrawPropertiesAdvanced()
     MapSelectboxGadget(i);
 
   /* draw textbutton gadgets */
-  MapTextbuttonGadget(ED_TEXTBUTTON_ID_SAVE_AS_TEMPLATE);
+  for (i=ED_TEXTBUTTON_ID_CHANGE_FIRST; i<=ED_TEXTBUTTON_ID_CHANGE_LAST; i++)
+    MapTextbuttonGadget(i);
+
+  /* draw graphicbutton gadgets */
+  for (i =  ED_GRAPHICBUTTON_ID_CHANGE_FIRST;
+       i <= ED_GRAPHICBUTTON_ID_CHANGE_LAST; i++)
+    MapGraphicbuttonGadget(i);
 
   /* draw drawing area gadgets */
   DrawPropertiesAdvancedDrawingAreas();
@@ -6039,19 +6439,19 @@ static void HandleDrawingAreas(struct GadgetInfo *gi)
        }
        else if (id == GADGET_ID_CUSTOM_CHANGE_TARGET)
        {
-         custom_element.change.target_element = new_element;
+         custom_element_change.target_element = new_element;
 
          CopyCustomElementPropertiesToGame(properties_element);
        }
        else if (id == GADGET_ID_CUSTOM_CHANGE_CONTENT)
        {
-         custom_element.change.content[sx][sy] = new_element;
+         custom_element_change.content[sx][sy] = new_element;
 
          CopyCustomElementPropertiesToGame(properties_element);
        }
        else if (id == GADGET_ID_CUSTOM_CHANGE_TRIGGER)
        {
-         custom_element.change.trigger_element = new_element;
+         custom_element_change.trigger_element = new_element;
 
          CopyCustomElementPropertiesToGame(properties_element);
        }
@@ -6166,11 +6566,11 @@ static void HandleDrawingAreas(struct GadgetInfo *gi)
       else if (id == GADGET_ID_CUSTOM_CONTENT)
        PickDrawingElement(button, custom_element.content[sx][sy]);
       else if (id == GADGET_ID_CUSTOM_CHANGE_TARGET)
-       PickDrawingElement(button, custom_element.change.target_element);
+       PickDrawingElement(button, custom_element_change.target_element);
       else if (id == GADGET_ID_CUSTOM_CHANGE_CONTENT)
-       PickDrawingElement(button, custom_element.change.content[sx][sy]);
+       PickDrawingElement(button, custom_element_change.content[sx][sy]);
       else if (id == GADGET_ID_CUSTOM_CHANGE_TRIGGER)
-       PickDrawingElement(button, custom_element.change.trigger_element);
+       PickDrawingElement(button, custom_element_change.trigger_element);
       else if (id == GADGET_ID_RANDOM_BACKGROUND)
        PickDrawingElement(button, random_placement_background_element);
       else if (id >= GADGET_ID_ELEMENT_CONTENT_0 &&
@@ -6216,7 +6616,7 @@ static void HandleCounterButtons(struct GadgetInfo *gi)
   }
 
   if (gadget_id == counterbutton_info[counter_id].gadget_id_text)
-    *counter_value = gi->text.number_value;
+    *counter_value = gi->textinput.number_value;
   else
     ModifyEditorCounter(counter_id, *counter_value + step);
 
@@ -6226,6 +6626,11 @@ static void HandleCounterButtons(struct GadgetInfo *gi)
       DrawElementContentAreas();
       break;
 
+    case ED_COUNTER_ID_ENVELOPE_XSIZE:
+    case ED_COUNTER_ID_ENVELOPE_YSIZE:
+      DrawEnvelopeTextArea();
+      break;
+
     case ED_COUNTER_ID_LEVEL_XSIZE:
     case ED_COUNTER_ID_LEVEL_YSIZE:
       lev_fieldx = level.fieldx;
@@ -6254,7 +6659,7 @@ static void HandleTextInputGadgets(struct GadgetInfo *gi)
 {
   int type_id = gi->custom_type_id;
 
-  strcpy(textinput_info[type_id].value, gi->text.value);
+  strcpy(textinput_info[type_id].value, gi->textinput.value);
 
   if (type_id == ED_TEXTINPUT_ID_ELEMENT_NAME)
   {
@@ -6264,6 +6669,13 @@ static void HandleTextInputGadgets(struct GadgetInfo *gi)
   }
 }
 
+static void HandleTextAreaGadgets(struct GadgetInfo *gi)
+{
+  int type_id = gi->custom_type_id;
+
+  strcpy(textarea_info[type_id].value, gi->textarea.value);
+}
+
 static void HandleSelectboxGadgets(struct GadgetInfo *gi)
 {
   int type_id = gi->custom_type_id;
@@ -6271,19 +6683,26 @@ static void HandleSelectboxGadgets(struct GadgetInfo *gi)
   *selectbox_info[type_id].value =
     selectbox_info[type_id].options[gi->selectbox.index].value;
 
-  if ((type_id >= ED_SELECTBOX_ID_CUSTOM_FIRST &&
-       type_id <= ED_SELECTBOX_ID_CUSTOM_LAST) ||
-      (type_id >= ED_SELECTBOX_ID_CHANGE_FIRST &&
-       type_id <= ED_SELECTBOX_ID_CHANGE_LAST))
+  if (type_id == ED_SELECTBOX_ID_SELECT_CHANGE_PAGE)
+  {
+    element_info[properties_element].current_change_page = gi->selectbox.index;
+
+    DrawPropertiesWindow();
+  }
+  else if ((type_id >= ED_SELECTBOX_ID_CUSTOM_FIRST &&
+           type_id <= ED_SELECTBOX_ID_CUSTOM_LAST) ||
+          (type_id >= ED_SELECTBOX_ID_CHANGE_FIRST &&
+           type_id <= ED_SELECTBOX_ID_CHANGE_LAST))
     CopyCustomElementPropertiesToGame(properties_element);
 }
 
 static void HandleTextbuttonGadgets(struct GadgetInfo *gi)
 {
   int type_id = gi->custom_type_id;
+  int i;
 
-  if (type_id >= ED_TEXTBUTTON_ID_PROPERTIES_INFO &&
-      type_id <= ED_TEXTBUTTON_ID_PROPERTIES_ADVANCED)
+  if (type_id >= ED_TEXTBUTTON_ID_PROPERTIES_FIRST &&
+      type_id <= ED_TEXTBUTTON_ID_PROPERTIES_LAST)
   {
     edit_mode_properties = gi->custom_type_id;
 
@@ -6300,6 +6719,56 @@ static void HandleTextbuttonGadgets(struct GadgetInfo *gi)
     if (new_template)
       Request("Tem- plate saved !", REQ_CONFIRM);
   }
+  else if (type_id == ED_TEXTBUTTON_ID_ADD_CHANGE_PAGE &&
+          custom_element.num_change_pages < MAX_CHANGE_PAGES)
+  {
+    struct ElementInfo *ei = &element_info[properties_element];
+
+    setElementChangePages(ei, ei->num_change_pages + 1);
+
+    /* set new change page to be new current change page */
+    ei->current_change_page = ei->num_change_pages - 1;
+    ei->change = &ei->change_page[ei->current_change_page];
+
+    setElementChangeInfoToDefaults(ei->change);
+
+    DrawPropertiesWindow();
+  }
+  else if (type_id == ED_TEXTBUTTON_ID_DEL_CHANGE_PAGE &&
+          custom_element.num_change_pages > MIN_CHANGE_PAGES)
+  {
+    struct ElementInfo *ei = &element_info[properties_element];
+
+    /* copy all change pages after change page to be deleted */
+    for (i = ei->current_change_page; i < ei->num_change_pages - 1; i++)
+      ei->change_page[i] = ei->change_page[i + 1];
+
+    setElementChangePages(ei, ei->num_change_pages - 1);
+
+    DrawPropertiesWindow();
+  }
+}
+
+static void HandleGraphicbuttonGadgets(struct GadgetInfo *gi)
+{
+  int type_id = gi->custom_type_id;
+
+  if (type_id == ED_GRAPHICBUTTON_ID_PREV_CHANGE_PAGE ||
+      type_id == ED_GRAPHICBUTTON_ID_NEXT_CHANGE_PAGE)
+  {
+    struct ElementInfo *ei = &element_info[properties_element];
+    int step = BUTTON_STEPSIZE(gi->event.button);
+
+    step *= (type_id == ED_GRAPHICBUTTON_ID_PREV_CHANGE_PAGE ? -1 : +1);
+    ei->current_change_page += step;
+
+    if (ei->current_change_page < 0)
+      ei->current_change_page = 0;
+    else if (ei->current_change_page >= ei->num_change_pages)
+      ei->current_change_page = ei->num_change_pages - 1;
+
+    DrawPropertiesWindow();
+  }
 }
 
 static void HandleRadiobuttons(struct GadgetInfo *gi)
@@ -6956,13 +7425,13 @@ static void HandleDrawingAreaInfo(struct GadgetInfo *gi)
                getElementInfoText(custom_element.content[sx][sy]));
     else if (id == GADGET_ID_CUSTOM_CHANGE_TARGET)
       DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FONT_TEXT_2, "%s",
-               getElementInfoText(custom_element.change.target_element));
+               getElementInfoText(custom_element_change.target_element));
     else if (id == GADGET_ID_CUSTOM_CHANGE_CONTENT)
       DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FONT_TEXT_2, "%s",
-               getElementInfoText(custom_element.change.content[sx][sy]));
+               getElementInfoText(custom_element_change.content[sx][sy]));
     else if (id == GADGET_ID_CUSTOM_CHANGE_TRIGGER)
       DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FONT_TEXT_2, "%s",
-               getElementInfoText(custom_element.change.trigger_element));
+               getElementInfoText(custom_element_change.trigger_element));
     else if (id == GADGET_ID_RANDOM_BACKGROUND)
       DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FONT_TEXT_2, "%s",
                getElementInfoText(random_placement_background_element));
index e17ef526412bcca6ec178c60922d4d50dacdb657..0b85e1eba468ddd2e0e3d7f5764181dc368744a3 100644 (file)
 #define LEVEL_HEADER_UNUSED    13      /* unused level header bytes  */
 #define LEVEL_CHUNK_CNT2_SIZE  160     /* size of level CNT2 chunk   */
 #define LEVEL_CHUNK_CNT2_UNUSED        11      /* unused CNT2 chunk bytes    */
+#define LEVEL_CHUNK_CNT3_HEADER        16      /* size of level CNT3 header  */
+#define LEVEL_CHUNK_CNT3_UNUSED        10      /* unused CNT3 chunk bytes    */
 #define LEVEL_CPART_CUS3_SIZE  134     /* size of CUS3 chunk part    */
 #define LEVEL_CPART_CUS3_UNUSED        15      /* unused CUS3 bytes / part   */
+#define LEVEL_CPART_CUS4_SIZE  ???     /* size of CUS4 chunk part    */
+#define LEVEL_CPART_CUS4_UNUSED        ???     /* unused CUS4 bytes / part   */
 #define TAPE_HEADER_SIZE       20      /* size of tape file header   */
 #define TAPE_HEADER_UNUSED     3       /* unused tape header bytes   */
 
-#define LEVEL_CHUNK_CUS3_SIZE(x) (2 + x * LEVEL_CPART_CUS3_SIZE)
+#define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
+#define LEVEL_CHUNK_CUS4_SIZE(x) (48 + 48 + (x) * 48)
 
 /* file identifier strings */
 #define LEVEL_COOKIE_TMPL      "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
 /* level file functions                                                      */
 /* ========================================================================= */
 
+void setElementChangePages(struct ElementInfo *ei, int change_pages)
+{
+  int change_page_size = sizeof(struct ElementChangeInfo);
+
+  ei->num_change_pages = MAX(1, change_pages);
+
+  ei->change_page =
+    checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
+
+  if (ei->current_change_page >= ei->num_change_pages)
+    ei->current_change_page = ei->num_change_pages - 1;
+
+  ei->change = &ei->change_page[ei->current_change_page];
+}
+
+void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
+{
+  int x, y;
+
+  change->can_change = FALSE;
+
+  change->events = CE_BITMASK_DEFAULT;
+  change->target_element = EL_EMPTY_SPACE;
+
+  change->delay_fixed = 0;
+  change->delay_random = 0;
+  change->delay_frames = -1;   /* later set to reliable default value */
+
+  change->trigger_element = EL_EMPTY_SPACE;
+
+  change->explode = FALSE;
+  change->use_content = FALSE;
+  change->only_complete = FALSE;
+  change->use_random_change = FALSE;
+  change->random = 0;
+  change->power = CP_NON_DESTRUCTIVE;
+
+  for(x=0; x<3; x++)
+    for(y=0; y<3; y++)
+      change->content[x][y] = EL_EMPTY_SPACE;
+
+  change->direct_action = 0;
+  change->other_action = 0;
+
+  change->pre_change_function = NULL;
+  change->change_function = NULL;
+  change->post_change_function = NULL;
+}
+
 static void setLevelInfoToDefaults(struct LevelInfo *level)
 {
   int i, j, x, y;
@@ -87,6 +141,10 @@ static void setLevelInfoToDefaults(struct LevelInfo *level)
   strcpy(level->name, NAMELESS_LEVEL_NAME);
   strcpy(level->author, ANONYMOUS_NAME);
 
+  level->envelope[0] = '\0';
+  level->envelope_xsize = MAX_ENVELOPE_XSIZE;
+  level->envelope_ysize = MAX_ENVELOPE_YSIZE;
+
   for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
     level->score[i] = 10;
 
@@ -100,11 +158,17 @@ static void setLevelInfoToDefaults(struct LevelInfo *level)
   level->field[0][0] = EL_PLAYER_1;
   level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
 
+  for (i=0; i < MAX_NUM_ELEMENTS; i++)
+  {
+    setElementChangePages(&element_info[i], 1);
+    setElementChangeInfoToDefaults(element_info[i].change);
+  }
+
   for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
   {
     int element = EL_CUSTOM_START + i;
 
-    for(j=0; j<MAX_ELEMENT_NAME_LEN + 1; j++)
+    for(j=0; j < MAX_ELEMENT_NAME_LEN + 1; j++)
       element_info[element].description[j] = '\0';
     if (element_info[element].custom_description != NULL)
       strncpy(element_info[element].description,
@@ -134,40 +198,19 @@ static void setLevelInfoToDefaults(struct LevelInfo *level)
       for(y=0; y<3; y++)
        element_info[element].content[x][y] = EL_EMPTY_SPACE;
 
-    element_info[element].change.events = CE_BITMASK_DEFAULT;
-    element_info[element].change.target_element = EL_EMPTY_SPACE;
-
-    element_info[element].change.delay_fixed = 0;
-    element_info[element].change.delay_random = 0;
-    element_info[element].change.delay_frames = -1;    /* use default */
-
-    element_info[element].change.trigger_element = EL_EMPTY_SPACE;
-
-    element_info[element].change.explode = FALSE;
-    element_info[element].change.use_content = FALSE;
-    element_info[element].change.only_complete = FALSE;
-    element_info[element].change.use_random_change = FALSE;
-    element_info[element].change.random = 0;
-    element_info[element].change.power = CP_NON_DESTRUCTIVE;
-
-    for(x=0; x<3; x++)
-      for(y=0; y<3; y++)
-       element_info[element].change.content[x][y] = EL_EMPTY_SPACE;
-
     element_info[element].access_type = 0;
     element_info[element].access_layer = 0;
     element_info[element].walk_to_action = 0;
     element_info[element].smash_targets = 0;
     element_info[element].deadliness = 0;
     element_info[element].consistency = 0;
-    element_info[element].change_player_action = 0;
-    element_info[element].change_collide_action = 0;
-    element_info[element].change_other_action = 0;
 
     element_info[element].can_explode_by_fire = FALSE;
     element_info[element].can_explode_smashed = FALSE;
     element_info[element].can_explode_impact = FALSE;
 
+    element_info[element].current_change_page = 0;
+
     /* start with no properties at all */
     for (j=0; j < NUM_EP_BITFIELDS; j++)
       Properties[element][j] = EP_BITMASK_DEFAULT;
@@ -378,6 +421,7 @@ static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
   num_contents = getFile8Bit(file);
   content_xsize = getFile8Bit(file);
   content_ysize = getFile8Bit(file);
+
   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
 
   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
@@ -410,6 +454,34 @@ static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
   return chunk_size;
 }
 
+static int LoadLevel_CNT3(FILE *file, int chunk_size, struct LevelInfo *level)
+{
+  int i;
+  int element;
+  int envelope_len;
+  int chunk_size_expected;
+
+  element = checkLevelElement(getFile16BitBE(file));
+  envelope_len = getFile16BitBE(file);
+  level->envelope_xsize = getFile8Bit(file);
+  level->envelope_ysize = getFile8Bit(file);
+
+  ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
+
+  chunk_size_expected = LEVEL_CHUNK_CNT3_HEADER + envelope_len;
+
+  if (chunk_size_expected != chunk_size)
+  {
+    ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
+    return chunk_size_expected;
+  }
+
+  for(i=0; i < envelope_len; i++)
+    level->envelope[i] = getFile8Bit(file);
+
+  return chunk_size;
+}
+
 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
 {
   int num_changed_custom_elements = getFile16BitBE(file);
@@ -454,7 +526,7 @@ static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
     int custom_target_element = getFile16BitBE(file);
 
     if (IS_CUSTOM_ELEMENT(element))
-      element_info[element].change.target_element = custom_target_element;
+      element_info[element].change->target_element = custom_target_element;
     else
       Error(ERR_WARN, "invalid custom element number %d", element);
   }
@@ -515,29 +587,29 @@ static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
        element_info[element].content[x][y] =
          checkLevelElement(getFile16BitBE(file));
 
-    element_info[element].change.events = getFile32BitBE(file);
+    element_info[element].change->events = getFile32BitBE(file);
 
-    element_info[element].change.target_element =
+    element_info[element].change->target_element =
       checkLevelElement(getFile16BitBE(file));
 
-    element_info[element].change.delay_fixed = getFile16BitBE(file);
-    element_info[element].change.delay_random = getFile16BitBE(file);
-    element_info[element].change.delay_frames = getFile16BitBE(file);
+    element_info[element].change->delay_fixed = getFile16BitBE(file);
+    element_info[element].change->delay_random = getFile16BitBE(file);
+    element_info[element].change->delay_frames = getFile16BitBE(file);
 
-    element_info[element].change.trigger_element =
+    element_info[element].change->trigger_element =
       checkLevelElement(getFile16BitBE(file));
 
-    element_info[element].change.explode = getFile8Bit(file);
-    element_info[element].change.use_content = getFile8Bit(file);
-    element_info[element].change.only_complete = getFile8Bit(file);
-    element_info[element].change.use_random_change = getFile8Bit(file);
+    element_info[element].change->explode = getFile8Bit(file);
+    element_info[element].change->use_content = getFile8Bit(file);
+    element_info[element].change->only_complete = getFile8Bit(file);
+    element_info[element].change->use_random_change = getFile8Bit(file);
 
-    element_info[element].change.random = getFile8Bit(file);
-    element_info[element].change.power = getFile8Bit(file);
+    element_info[element].change->random = getFile8Bit(file);
+    element_info[element].change->power = getFile8Bit(file);
 
     for(y=0; y<3; y++)
       for(x=0; x<3; x++)
-       element_info[element].change.content[x][y] =
+       element_info[element].change->content[x][y] =
          checkLevelElement(getFile16BitBE(file));
 
     element_info[element].slippery_type = getFile8Bit(file);
@@ -552,6 +624,109 @@ static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
   return chunk_size;
 }
 
+static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
+{
+  struct ElementInfo *ei;
+  int chunk_size_expected;
+  int element;
+  int i, x, y;
+
+  element = getFile16BitBE(file);
+
+  if (!IS_CUSTOM_ELEMENT(element))
+  {
+    Error(ERR_WARN, "invalid custom element number %d", element);
+
+    element = EL_DEFAULT;      /* dummy element used for artwork config */
+  }
+
+  ei = &element_info[element];
+
+  for(i=0; i < MAX_ELEMENT_NAME_LEN; i++)
+    ei->description[i] = getFile8Bit(file);
+  ei->description[MAX_ELEMENT_NAME_LEN] = 0;
+
+  Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
+  ReadUnusedBytesFromFile(file, 4);    /* reserved for more base properties */
+
+  ei->num_change_pages = getFile8Bit(file);
+
+  /* some free bytes for future base property values and padding */
+  ReadUnusedBytesFromFile(file, 5);
+
+  chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
+  if (chunk_size_expected != chunk_size)
+  {
+    ReadUnusedBytesFromFile(file, chunk_size - 48);
+    return chunk_size_expected;
+  }
+
+  /* read custom property values */
+
+  ei->use_gfx_element = getFile8Bit(file);
+  ei->gfx_element = checkLevelElement(getFile16BitBE(file));
+
+  ei->collect_score = getFile8Bit(file);
+  ei->collect_count = getFile8Bit(file);
+
+  ei->push_delay_fixed = getFile16BitBE(file);
+  ei->push_delay_random = getFile16BitBE(file);
+  ei->move_delay_fixed = getFile16BitBE(file);
+  ei->move_delay_random = getFile16BitBE(file);
+
+  ei->move_pattern = getFile16BitBE(file);
+  ei->move_direction_initial = getFile8Bit(file);
+  ei->move_stepsize = getFile8Bit(file);
+
+  ei->slippery_type = getFile8Bit(file);
+
+  for(y=0; y<3; y++)
+    for(x=0; x<3; x++)
+      ei->content[x][y] = checkLevelElement(getFile16BitBE(file));
+
+  /* some free bytes for future custom property values and padding */
+  ReadUnusedBytesFromFile(file, 12);
+
+  /* read change property values */
+
+  for (i=0; i < ei->num_change_pages; i++)
+  {
+    struct ElementChangeInfo *change = &ei->change_page[i];
+
+    change->events = getFile32BitBE(file);
+
+    change->target_element = checkLevelElement(getFile16BitBE(file));
+
+    change->delay_fixed = getFile16BitBE(file);
+    change->delay_random = getFile16BitBE(file);
+    change->delay_frames = getFile16BitBE(file);
+
+    change->trigger_element = checkLevelElement(getFile16BitBE(file));
+
+    change->explode = getFile8Bit(file);
+    change->use_content = getFile8Bit(file);
+    change->only_complete = getFile8Bit(file);
+    change->use_random_change = getFile8Bit(file);
+
+    change->random = getFile8Bit(file);
+    change->power = getFile8Bit(file);
+
+    for(y=0; y<3; y++)
+      for(x=0; x<3; x++)
+       change->content[x][y] = checkLevelElement(getFile16BitBE(file));
+
+    change->can_change = getFile8Bit(file);
+
+    /* some free bytes for future change property values and padding */
+    ReadUnusedBytesFromFile(file, 9);
+  }
+
+  /* mark this custom element as modified */
+  ei->modified_settings = TRUE;
+
+  return chunk_size;
+}
+
 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
 {
   char cookie[MAX_LINE_LEN];
@@ -632,9 +807,11 @@ void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
       { "BODY", -1,                    LoadLevel_BODY },
       { "CONT", -1,                    LoadLevel_CONT },
       { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
+      { "CNT3", -1,                    LoadLevel_CNT3 },
       { "CUS1", -1,                    LoadLevel_CUS1 },
       { "CUS2", -1,                    LoadLevel_CUS2 },
       { "CUS3", -1,                    LoadLevel_CUS3 },
+      { "CUS4", -1,                    LoadLevel_CUS4 },
       {  NULL,  0,                     NULL }
     };
 
@@ -680,6 +857,185 @@ void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
   fclose(file);
 }
 
+#if 1
+
+static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
+{
+  if (leveldir_current == NULL)                /* only when dumping level */
+    return;
+
+  /* determine correct game engine version of current level */
+  if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
+      IS_LEVELCLASS_USER(leveldir_current))
+  {
+#if 0
+    printf("\n::: This level is private or contributed: '%s'\n", filename);
+#endif
+
+    /* For user contributed and private levels, use the version of
+       the game engine the levels were created for.
+       Since 2.0.1, the game engine version is now directly stored
+       in the level file (chunk "VERS"), so there is no need anymore
+       to set the game version from the file version (except for old,
+       pre-2.0 levels, where the game version is still taken from the
+       file format version used to store the level -- see above). */
+
+    /* do some special adjustments to support older level versions */
+    if (level->file_version == FILE_VERSION_1_0)
+    {
+      Error(ERR_WARN, "level file '%s'has version number 1.0", filename);
+      Error(ERR_WARN, "using high speed movement for player");
+
+      /* player was faster than monsters in (pre-)1.0 levels */
+      level->double_speed = TRUE;
+    }
+
+    /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
+    if (level->game_version == VERSION_IDENT(2,0,1))
+      level->em_slippery_gems = TRUE;
+  }
+  else
+  {
+#if 0
+    printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
+          leveldir_current->sort_priority, filename);
+#endif
+
+    /* Always use the latest version of the game engine for all but
+       user contributed and private levels; this allows for actual
+       corrections in the game engine to take effect for existing,
+       converted levels (from "classic" or other existing games) to
+       make the game emulation more accurate, while (hopefully) not
+       breaking existing levels created from other players. */
+
+    level->game_version = GAME_VERSION_ACTUAL;
+
+    /* Set special EM style gems behaviour: EM style gems slip down from
+       normal, steel and growing wall. As this is a more fundamental change,
+       it seems better to set the default behaviour to "off" (as it is more
+       natural) and make it configurable in the level editor (as a property
+       of gem style elements). Already existing converted levels (neither
+       private nor contributed levels) are changed to the new behaviour. */
+
+    if (level->file_version < FILE_VERSION_2_0)
+      level->em_slippery_gems = TRUE;
+  }
+}
+
+static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
+{
+  int i, j;
+
+  /* map custom element change events that have changed in newer versions
+     (these following values were accidentally changed in version 3.0.1) */
+  if (level->game_version <= VERSION_IDENT(3,0,0))
+  {
+    for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
+    {
+      int element = EL_CUSTOM_START + i;
+
+      /* order of checking and copying events to be mapped is important */
+      for (j=CE_BY_OTHER_ACTION; j >= CE_BY_PLAYER; j--)
+      {
+       if (HAS_CHANGE_EVENT(element, j - 2))
+       {
+         SET_CHANGE_EVENT(element, j - 2, FALSE);
+         SET_CHANGE_EVENT(element, j, TRUE);
+       }
+      }
+
+      /* order of checking and copying events to be mapped is important */
+      for (j=CE_OTHER_GETS_COLLECTED; j >= CE_COLLISION; j--)
+      {
+       if (HAS_CHANGE_EVENT(element, j - 1))
+       {
+         SET_CHANGE_EVENT(element, j - 1, FALSE);
+         SET_CHANGE_EVENT(element, j, TRUE);
+       }
+      }
+    }
+  }
+
+  /* some custom element change events get mapped since version 3.0.3 */
+  for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
+  {
+    int element = EL_CUSTOM_START + i;
+
+    if (HAS_CHANGE_EVENT(element, CE_BY_PLAYER) ||
+       HAS_CHANGE_EVENT(element, CE_BY_COLLISION))
+    {
+      SET_CHANGE_EVENT(element, CE_BY_PLAYER, FALSE);
+      SET_CHANGE_EVENT(element, CE_BY_COLLISION, FALSE);
+
+      SET_CHANGE_EVENT(element, CE_BY_DIRECT_ACTION, TRUE);
+    }
+  }
+
+  /* initialize "can_change" field for old levels with only one change page */
+  if (level->game_version <= VERSION_IDENT(3,0,2))
+  {
+    for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
+    {
+      int element = EL_CUSTOM_START + i;
+
+      if (CAN_CHANGE(element))
+       element_info[element].change->can_change = TRUE;
+    }
+  }
+
+  /* initialize element properties for level editor etc. */
+  InitElementPropertiesEngine(level->game_version);
+}
+
+static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
+{
+  int x, y;
+
+  /* map elements that have changed in newer versions */
+  for(y=0; y<level->fieldy; y++)
+  {
+    for(x=0; x<level->fieldx; x++)
+    {
+      int element = level->field[x][y];
+
+      if (level->game_version <= VERSION_IDENT(2,2,0))
+      {
+       /* map game font elements */
+       element = (element == EL_CHAR('[')  ? EL_CHAR_AUMLAUT :
+                  element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
+                  element == EL_CHAR(']')  ? EL_CHAR_UUMLAUT :
+                  element == EL_CHAR('^')  ? EL_CHAR_COPYRIGHT : element);
+      }
+
+      if (level->game_version < VERSION_IDENT(3,0,0))
+      {
+       /* map Supaplex gravity tube elements */
+       element = (element == EL_SP_GRAVITY_PORT_LEFT  ? EL_SP_PORT_LEFT  :
+                  element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
+                  element == EL_SP_GRAVITY_PORT_UP    ? EL_SP_PORT_UP    :
+                  element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
+                  element);
+      }
+
+      level->field[x][y] = element;
+    }
+  }
+
+  /* copy elements to runtime playfield array */
+  for(x=0; x<MAX_LEV_FIELDX; x++)
+    for(y=0; y<MAX_LEV_FIELDY; y++)
+      Feld[x][y] = level->field[x][y];
+
+  /* initialize level size variables for faster access */
+  lev_fieldx = level->fieldx;
+  lev_fieldy = level->fieldy;
+
+  /* determine border element for this level */
+  SetBorderElement();
+}
+
+#else
+
 static void LoadLevel_InitLevel(struct LevelInfo *level, char *filename)
 {
   int i, j, x, y;
@@ -804,6 +1160,18 @@ static void LoadLevel_InitLevel(struct LevelInfo *level, char *filename)
     }
   }
 
+  /* initialize "can_change" field for old levels with only one change page */
+  if (level->game_version <= VERSION_IDENT(3,0,2))
+  {
+    for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
+    {
+      int element = EL_CUSTOM_START + i;
+
+      if (CAN_CHANGE(element))
+       element_info[element].change->can_change = TRUE;
+    }
+  }
+
   /* copy elements to runtime playfield array */
   for(x=0; x<MAX_LEV_FIELDX; x++)
     for(y=0; y<MAX_LEV_FIELDY; y++)
@@ -820,12 +1188,17 @@ static void LoadLevel_InitLevel(struct LevelInfo *level, char *filename)
   InitElementPropertiesEngine(level->game_version);
 }
 
+#endif
+
 void LoadLevelTemplate(int level_nr)
 {
   char *filename = getLevelFilename(level_nr);
 
   LoadLevelFromFilename(&level_template, filename);
 
+  LoadLevel_InitVersion(&level, filename);
+  LoadLevel_InitElements(&level, filename);
+
   ActivateLevelTemplate();
 }
 
@@ -838,7 +1211,13 @@ void LoadLevel(int level_nr)
   if (level.use_custom_template)
     LoadLevelTemplate(-1);
 
+#if 1
+  LoadLevel_InitVersion(&level, filename);
+  LoadLevel_InitElements(&level, filename);
+  LoadLevel_InitPlayfield(&level, filename);
+#else
   LoadLevel_InitLevel(&level, filename);
+#endif
 }
 
 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
@@ -974,6 +1353,22 @@ static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
        putFile16BitBE(file, content_array[i][x][y]);
 }
 
+static void SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
+{
+  int i;
+  int envelope_len = strlen(level->envelope) + 1;
+
+  putFile16BitBE(file, element);
+  putFile16BitBE(file, envelope_len);
+  putFile8Bit(file, level->envelope_xsize);
+  putFile8Bit(file, level->envelope_ysize);
+
+  WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
+
+  for(i=0; i < envelope_len; i++)
+    putFile8Bit(file, level->envelope[i]);
+}
+
 #if 0
 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
                           int num_changed_custom_elements)
@@ -1015,12 +1410,12 @@ static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
   {
     int element = EL_CUSTOM_START + i;
 
-    if (element_info[element].change.target_element != EL_EMPTY_SPACE)
+    if (element_info[element].change->target_element != EL_EMPTY_SPACE)
     {
       if (check < num_changed_custom_elements)
       {
        putFile16BitBE(file, element);
-       putFile16BitBE(file, element_info[element].change.target_element);
+       putFile16BitBE(file, element_info[element].change->target_element);
       }
 
       check++;
@@ -1032,6 +1427,7 @@ static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
 }
 #endif
 
+#if 0
 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
                           int num_changed_custom_elements)
 {
@@ -1076,27 +1472,27 @@ static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
          for(x=0; x<3; x++)
            putFile16BitBE(file, element_info[element].content[x][y]);
 
-       putFile32BitBE(file, element_info[element].change.events);
+       putFile32BitBE(file, element_info[element].change->events);
 
-       putFile16BitBE(file, element_info[element].change.target_element);
+       putFile16BitBE(file, element_info[element].change->target_element);
 
-       putFile16BitBE(file, element_info[element].change.delay_fixed);
-       putFile16BitBE(file, element_info[element].change.delay_random);
-       putFile16BitBE(file, element_info[element].change.delay_frames);
+       putFile16BitBE(file, element_info[element].change->delay_fixed);
+       putFile16BitBE(file, element_info[element].change->delay_random);
+       putFile16BitBE(file, element_info[element].change->delay_frames);
 
-       putFile16BitBE(file, element_info[element].change.trigger_element);
+       putFile16BitBE(file, element_info[element].change->trigger_element);
 
-       putFile8Bit(file, element_info[element].change.explode);
-       putFile8Bit(file, element_info[element].change.use_content);
-       putFile8Bit(file, element_info[element].change.only_complete);
-       putFile8Bit(file, element_info[element].change.use_random_change);
+       putFile8Bit(file, element_info[element].change->explode);
+       putFile8Bit(file, element_info[element].change->use_content);
+       putFile8Bit(file, element_info[element].change->only_complete);
+       putFile8Bit(file, element_info[element].change->use_random_change);
 
-       putFile8Bit(file, element_info[element].change.random);
-       putFile8Bit(file, element_info[element].change.power);
+       putFile8Bit(file, element_info[element].change->random);
+       putFile8Bit(file, element_info[element].change->power);
 
        for(y=0; y<3; y++)
          for(x=0; x<3; x++)
-           putFile16BitBE(file, element_info[element].change.content[x][y]);
+           putFile16BitBE(file, element_info[element].change->content[x][y]);
 
        putFile8Bit(file, element_info[element].slippery_type);
 
@@ -1111,12 +1507,90 @@ static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
   if (check != num_changed_custom_elements)    /* should not happen */
     Error(ERR_WARN, "inconsistent number of custom element properties");
 }
+#endif
+
+static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
+{
+  struct ElementInfo *ei = &element_info[element];
+  int i, x, y;
+
+  putFile16BitBE(file, element);
+
+  for(i=0; i < MAX_ELEMENT_NAME_LEN; i++)
+    putFile8Bit(file, ei->description[i]);
+
+  putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
+  WriteUnusedBytesToFile(file, 4);     /* reserved for more base properties */
+
+  putFile8Bit(file, ei->num_change_pages);
+
+  /* some free bytes for future base property values and padding */
+  WriteUnusedBytesToFile(file, 5);
+
+  /* write custom property values */
+
+  putFile8Bit(file, ei->use_gfx_element);
+  putFile16BitBE(file, ei->gfx_element);
+
+  putFile8Bit(file, ei->collect_score);
+  putFile8Bit(file, ei->collect_count);
+
+  putFile16BitBE(file, ei->push_delay_fixed);
+  putFile16BitBE(file, ei->push_delay_random);
+  putFile16BitBE(file, ei->move_delay_fixed);
+  putFile16BitBE(file, ei->move_delay_random);
+
+  putFile16BitBE(file, ei->move_pattern);
+  putFile8Bit(file, ei->move_direction_initial);
+  putFile8Bit(file, ei->move_stepsize);
+
+  putFile8Bit(file, ei->slippery_type);
+
+  for(y=0; y<3; y++)
+    for(x=0; x<3; x++)
+      putFile16BitBE(file, ei->content[x][y]);
+
+  /* some free bytes for future custom property values and padding */
+  WriteUnusedBytesToFile(file, 12);
+
+  /* write change property values */
+
+  for (i=0; i < ei->num_change_pages; i++)
+  {
+    struct ElementChangeInfo *change = &ei->change_page[i];
+
+    putFile32BitBE(file, change->events);
+
+    putFile16BitBE(file, change->target_element);
+
+    putFile16BitBE(file, change->delay_fixed);
+    putFile16BitBE(file, change->delay_random);
+    putFile16BitBE(file, change->delay_frames);
+
+    putFile16BitBE(file, change->trigger_element);
+
+    putFile8Bit(file, change->explode);
+    putFile8Bit(file, change->use_content);
+    putFile8Bit(file, change->only_complete);
+    putFile8Bit(file, change->use_random_change);
+
+    putFile8Bit(file, change->random);
+    putFile8Bit(file, change->power);
+
+    for(y=0; y<3; y++)
+      for(x=0; x<3; x++)
+       putFile16BitBE(file, change->content[x][y]);
+
+    putFile8Bit(file, change->can_change);
+
+    /* some free bytes for future change property values and padding */
+    WriteUnusedBytesToFile(file, 9);
+  }
+}
 
 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
 {
   int body_chunk_size;
-  int num_changed_custom_elements = 0;
-  int level_chunk_CUS3_size;
   int i, x, y;
   FILE *file;
 
@@ -1153,12 +1627,6 @@ static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
   body_chunk_size =
     level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
 
-  /* check for non-standard custom elements and calculate "CUS3" chunk size */
-  for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
-    if (element_info[EL_CUSTOM_START + i].modified_settings)
-      num_changed_custom_elements++;
-  level_chunk_CUS3_size = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
-
   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
 
@@ -1187,10 +1655,30 @@ static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
     SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
   }
 
-  if (num_changed_custom_elements > 0 && !level->use_custom_template)
+  /* check for envelope content */
+  if (strlen(level->envelope) > 0)
+  {
+    int envelope_len = strlen(level->envelope) + 1;
+
+    putFileChunkBE(file, "CNT3", LEVEL_CHUNK_CNT3_HEADER + envelope_len);
+    SaveLevel_CNT3(file, level, EL_ENVELOPE);
+  }
+
+  /* check for non-default custom elements (unless using template level) */
+  if (!level->use_custom_template)
   {
-    putFileChunkBE(file, "CUS3", level_chunk_CUS3_size);
-    SaveLevel_CUS3(file, level, num_changed_custom_elements);
+    for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
+    {
+      int element = EL_CUSTOM_START + i;
+
+      if (element_info[element].modified_settings)
+      {
+       int num_change_pages = element_info[element].num_change_pages;
+
+       putFileChunkBE(file, "CUS4", LEVEL_CHUNK_CUS4_SIZE(num_change_pages));
+       SaveLevel_CUS4(file, level, element);
+      }
+    }
   }
 
   fclose(file);
@@ -1839,8 +2327,10 @@ void SaveScore(int level_nr)
 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH   6
 #define SETUP_TOKEN_EDITOR_EL_CHARS            7
 #define SETUP_TOKEN_EDITOR_EL_CUSTOM           8
+#define SETUP_TOKEN_EDITOR_EL_CUSTOM_MORE      9
+#define SETUP_TOKEN_EDITOR_EL_HEADLINES                10
 
-#define NUM_EDITOR_SETUP_TOKENS                        9
+#define NUM_EDITOR_SETUP_TOKENS                        11
 
 /* shortcut setup */
 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME         0
@@ -1926,6 +2416,7 @@ static struct TokenInfo editor_setup_tokens[] =
   { TYPE_SWITCH, &sei.el_chars,                "editor.el_chars"               },
   { TYPE_SWITCH, &sei.el_custom,       "editor.el_custom"              },
   { TYPE_SWITCH, &sei.el_custom_more,  "editor.el_custom_more"         },
+  { TYPE_SWITCH, &sei.el_headlines,    "editor.el_headlines"           },
 };
 
 static struct TokenInfo shortcut_setup_tokens[] =
@@ -2023,6 +2514,8 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
   si->editor.el_custom = TRUE;
   si->editor.el_custom_more = FALSE;
 
+  si->editor.el_headlines = TRUE;
+
   si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
   si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
   si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
index 5f5f67dc74cebda330e7159e949724e9e337a4b9..1ae371db1f3dee3ce13f253c3bdf77e64f697903 100644 (file)
@@ -16,6 +16,9 @@
 
 #include "main.h"
 
+void setElementChangePages(struct ElementInfo *, int);
+void setElementChangeInfoToDefaults(struct ElementChangeInfo *);
+
 boolean LevelFileExists(int);
 void LoadLevelFromFilename(struct LevelInfo *, char *);
 void LoadLevel(int);
index 74c934d291b4284f04e963b5b53d3f911edb69a8..91694d2d7ea0e2ab2fd617081c89a046c14e5105 100644 (file)
@@ -171,7 +171,6 @@ static void TestIfElementTouchesCustomElement(int, int);
 
 static boolean CheckTriggeredElementChange(int, int, int, int);
 static boolean CheckElementChange(int, int, int, int);
-static void ChangeElementNow(int, int, int);
 
 static void PlaySoundLevel(int, int, int);
 static void PlaySoundLevelNearest(int, int, int);
@@ -216,7 +215,7 @@ struct ChangingElementInfo
   void (*post_change_function)(int x, int y);
 };
 
-static struct ChangingElementInfo changing_element_list[] =
+static struct ChangingElementInfo change_delay_list[] =
 {
   {
     EL_NUT_BREAKING,
@@ -415,14 +414,16 @@ collect_count_list[] =
   { EL_UNDEFINED,              0 },
 };
 
-static boolean changing_element[MAX_NUM_ELEMENTS];
 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
 
-#define IS_AUTO_CHANGING(e)    (changing_element[e])
+#define IS_AUTO_CHANGING(e)    (element_info[e].change_events & \
+                                CH_EVENT_BIT(CE_DELAY))
 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
 #define IS_CHANGING(x, y)      (IS_AUTO_CHANGING(Feld[x][y]) || \
                                 IS_JUST_CHANGING(x, y))
 
+#define CE_PAGE(e, ce)         (element_info[e].event_page[ce])
+
 
 void GetPlayerConfig()
 {
@@ -742,7 +743,7 @@ void DrawGameDoorValues()
 
 static void InitGameEngine()
 {
-  int i;
+  int i, j, k;
 
   /* set game engine from tape file when re-playing, else from level file */
   game.engine_version = (tape.playing ? tape.engine_version :
@@ -752,11 +753,11 @@ static void InitGameEngine()
   InitElementPropertiesEngine(game.engine_version);
 
 #if 0
-    printf("level %d: level version == %06d\n", level_nr, level.game_version);
-    printf("          tape version == %06d [%s] [file: %06d]\n",
-          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
-          tape.file_version);
-    printf("       => game.engine_version == %06d\n", game.engine_version);
+  printf("level %d: level version == %06d\n", level_nr, level.game_version);
+  printf("          tape version == %06d [%s] [file: %06d]\n",
+        tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
+        tape.file_version);
+  printf("       => game.engine_version == %06d\n", game.engine_version);
 #endif
 
   /* ---------- initialize player's initial move delay --------------------- */
@@ -773,78 +774,82 @@ static void InitGameEngine()
   /* ---------- initialize changing elements ------------------------------- */
 
   /* initialize changing elements information */
-  for (i=0; i<MAX_NUM_ELEMENTS; i++)
+  for (i=0; i < MAX_NUM_ELEMENTS; i++)
   {
-#if 1
-    element_info[i].change.pre_change_function = NULL;
-    element_info[i].change.change_function = NULL;
-    element_info[i].change.post_change_function = NULL;
+    struct ElementInfo *ei = &element_info[i];
+
+    /* this pointer might have been changed in the level editor */
+    ei->change = &ei->change_page[0];
 
     if (!IS_CUSTOM_ELEMENT(i))
     {
-      element_info[i].change.target_element = EL_EMPTY_SPACE;
-      element_info[i].change.delay_fixed = 0;
-      element_info[i].change.delay_random = 0;
-      element_info[i].change.delay_frames = 1;
+      ei->change->target_element = EL_EMPTY_SPACE;
+      ei->change->delay_fixed = 0;
+      ei->change->delay_random = 0;
+      ei->change->delay_frames = 1;
     }
 
-    changing_element[i] = FALSE;
-#else
-    changing_element[i].base_element = EL_UNDEFINED;
-    changing_element[i].next_element = EL_UNDEFINED;
-    changing_element[i].change_delay = -1;
-    changing_element[i].pre_change_function = NULL;
-    changing_element[i].change_function = NULL;
-    changing_element[i].post_change_function = NULL;
-#endif
+    ei->change_events = CE_BITMASK_DEFAULT;
+    for (j=0; j < NUM_CHANGE_EVENTS; j++)
+    {
+      ei->event_page_num[j] = 0;
+      ei->event_page[j] = &ei->change_page[0];
+    }
   }
 
   /* add changing elements from pre-defined list */
-  for (i=0; changing_element_list[i].element != EL_UNDEFINED; i++)
+  for (i=0; change_delay_list[i].element != EL_UNDEFINED; i++)
   {
-    int element = changing_element_list[i].element;
-    struct ChangingElementInfo *ce = &changing_element_list[i];
-    struct ElementChangeInfo *change = &element_info[element].change;
+    struct ChangingElementInfo *ch_delay = &change_delay_list[i];
+    struct ElementInfo *ei = &element_info[ch_delay->element];
+
+    ei->change->target_element       = ch_delay->target_element;
+    ei->change->delay_fixed          = ch_delay->change_delay;
+
+    ei->change->pre_change_function  = ch_delay->pre_change_function;
+    ei->change->change_function      = ch_delay->change_function;
+    ei->change->post_change_function = ch_delay->post_change_function;
+
+    ei->change_events |= CH_EVENT_BIT(CE_DELAY);
+  }
 
 #if 1
-    change->target_element       = ce->target_element;
-    change->delay_fixed          = ce->change_delay;
-    change->pre_change_function  = ce->pre_change_function;
-    change->change_function      = ce->change_function;
-    change->post_change_function = ce->post_change_function;
+  /* add change events from custom element configuration */
+  for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
+  {
+    struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
 
-    changing_element[element] = TRUE;
-#else
-    changing_element[element].base_element         = ce->base_element;
-    changing_element[element].next_element         = ce->next_element;
-    changing_element[element].change_delay         = ce->change_delay;
-    changing_element[element].pre_change_function  = ce->pre_change_function;
-    changing_element[element].change_function      = ce->change_function;
-    changing_element[element].post_change_function = ce->post_change_function;
-#endif
+    for (j=0; j < ei->num_change_pages; j++)
+    {
+      if (!ei->change_page[j].can_change)
+       continue;
+
+      for (k=0; k < NUM_CHANGE_EVENTS; k++)
+      {
+       /* only add event page for the first page found with this event */
+       if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
+           !(ei->change_events & CH_EVENT_BIT(k)))
+       {
+         ei->change_events |= CH_EVENT_BIT(k);
+         ei->event_page_num[k] = j;
+         ei->event_page[k] = &ei->change_page[j];
+       }
+      }
+    }
   }
 
-  /* add changing elements from custom element configuration */
+#else
+
+  /* add change events from custom element configuration */
   for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
   {
     int element = EL_CUSTOM_START + i;
-#if 0
-    struct ElementChangeInfo *change = &element_info[element].change;
-#endif
 
     /* only add custom elements that change after fixed/random frame delay */
-    if (!CAN_CHANGE(element) || !HAS_CHANGE_EVENT(element, CE_DELAY))
-      continue;
-
-#if 1
-    changing_element[element] = TRUE;
-#else
-    changing_element[element].base_element = element;
-    changing_element[element].next_element = change->target_element;
-    changing_element[element].change_delay = (change->delay_fixed *
-                                             change->delay_frames);
-#endif
+    if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
+      element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
   }
+#endif
 
   /* ---------- initialize trigger events ---------------------------------- */
 
@@ -852,11 +857,32 @@ static void InitGameEngine()
   for (i=0; i<MAX_NUM_ELEMENTS; i++)
     trigger_events[i] = EP_BITMASK_DEFAULT;
 
+#if 1
+  /* add trigger events from element change event properties */
+  for (i=0; i<MAX_NUM_ELEMENTS; i++)
+  {
+    struct ElementInfo *ei = &element_info[i];
+
+    for (j=0; j < ei->num_change_pages; j++)
+    {
+      if (!ei->change_page->can_change)
+       continue;
+
+      if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
+      {
+       int trigger_element = ei->change_page[j].trigger_element;
+
+       trigger_events[trigger_element] |= ei->change_page[j].events;
+      }
+    }
+  }
+#else
   /* add trigger events from element change event properties */
   for (i=0; i<MAX_NUM_ELEMENTS; i++)
-    if (HAS_CHANGE_EVENT(i, CE_BY_OTHER))
-      trigger_events[element_info[i].change.trigger_element] |=
-       element_info[i].change.events;
+    if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
+      trigger_events[element_info[i].change->trigger_element] |=
+       element_info[i].change->events;
+#endif
 
   /* ---------- initialize push delay -------------------------------------- */
 
@@ -1064,7 +1090,7 @@ void InitGame()
       JustStopped[x][y] = 0;
       Stop[x][y] = FALSE;
       Pushed[x][y] = FALSE;
-      Changing[x][y] = FALSE;
+      Changed[x][y] = FALSE;
       ExplodePhase[x][y] = 0;
       ExplodeField[x][y] = EX_NO_EXPLOSION;
 
@@ -1236,16 +1262,19 @@ void InitGame()
 
       if (CAN_CHANGE(element))
       {
-       content = element_info[element].change.target_element;
-       is_player = ELEM_IS_PLAYER(content);
-
-       if (is_player && (found_rating < 3 || element < found_element))
+       for (i=0; i < element_info[element].num_change_pages; i++)
        {
-         start_x = x;
-         start_y = y;
+         content = element_info[element].change_page[i].target_element;
+         is_player = ELEM_IS_PLAYER(content);
 
-         found_rating = 3;
-         found_element = element;
+         if (is_player && (found_rating < 3 || element < found_element))
+         {
+           start_x = x;
+           start_y = y;
+
+           found_rating = 3;
+           found_element = element;
+         }
        }
       }
 
@@ -1266,16 +1295,19 @@ void InitGame()
        if (!CAN_CHANGE(element))
          continue;
 
-       content = element_info[element].change.content[xx][yy];
-       is_player = ELEM_IS_PLAYER(content);
-
-       if (is_player && (found_rating < 1 || element < found_element))
+       for (i=0; i < element_info[element].num_change_pages; i++)
        {
-         start_x = x + xx - 1;
-         start_y = y + yy - 1;
+         content = element_info[element].change_page[i].content[xx][yy];
+         is_player = ELEM_IS_PLAYER(content);
 
-         found_rating = 1;
-         found_element = element;
+         if (is_player && (found_rating < 1 || element < found_element))
+         {
+           start_x = x + xx - 1;
+           start_y = y + yy - 1;
+
+           found_rating = 1;
+           found_element = element;
+         }
        }
       }
     }
@@ -1549,8 +1581,13 @@ void GameWon()
   if (local_player->MovPos)
     return;
 
+#if 1
+  if (tape.auto_play)          /* tape might already be stopped here */
+    tape.auto_play_level_solved = TRUE;
+#else
   if (tape.playing && tape.auto_play)
     tape.auto_play_level_solved = TRUE;
+#endif
 
   local_player->LevelSolved = FALSE;
 
@@ -2865,24 +2902,12 @@ void Impact(int x, int y)
     PlaySoundLevel(x, y, SND_PEARL_BREAKING);
     return;
   }
-#if 1
   else if (impact && CheckElementChange(x, y, element, CE_IMPACT))
   {
     PlaySoundLevelElementAction(x, y, element, ACTION_IMPACT);
 
     return;
   }
-#else
-  else if (impact && CAN_CHANGE(element) &&
-          HAS_CHANGE_EVENT(element, CE_IMPACT))
-  {
-    PlaySoundLevelElementAction(x, y, element, ACTION_IMPACT);
-
-    ChangeElementNow(x, y, element);
-
-    return;
-  }
-#endif
 
   if (impact && element == EL_AMOEBA_DROP)
   {
@@ -3014,17 +3039,10 @@ void Impact(int x, int y)
        {
          ToggleLightSwitch(x, y + 1);
        }
-#if 1
        else
        {
          CheckElementChange(x, y + 1, smashed, CE_SMASHED);
        }
-#else
-       else if (CAN_CHANGE(smashed) && HAS_CHANGE_EVENT(smashed, CE_SMASHED))
-       {
-         ChangeElementNow(x, y + 1, smashed);
-       }
-#endif
       }
       else
       {
@@ -3620,7 +3638,8 @@ void StartMoving(int x, int y)
 
   if (CAN_FALL(element) && y < lev_fieldy - 1)
   {
-    if ((x>0 && IS_PLAYER(x-1, y)) || (x<lev_fieldx-1 && IS_PLAYER(x+1, y)))
+    if ((x > 0 && IS_PLAYER(x - 1, y)) ||
+       (x < lev_fieldx-1 && IS_PLAYER(x + 1, y)))
       if (JustBeingPushed(x, y))
        return;
 
@@ -4021,7 +4040,8 @@ void StartMoving(int x, int y)
 
     Moving2Blocked(x, y, &newx, &newy);        /* get next screen position */
 
-    if (DONT_COLLIDE_WITH(element) && IS_PLAYER(newx, newy) &&
+    if (DONT_COLLIDE_WITH(element) &&
+       IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
        !PLAYER_PROTECTED(newx, newy))
     {
 #if 1
@@ -5312,7 +5332,7 @@ static void ChangeElementNowExt(int x, int y, int target_element)
 {
   /* check if element under player changes from accessible to unaccessible
      (needed for special case of dropping element which then changes) */
-  if (IS_PLAYER(x, y) &&
+  if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y) &&
       IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
   {
     Bang(x, y);
@@ -5322,6 +5342,8 @@ static void ChangeElementNowExt(int x, int y, int target_element)
   RemoveField(x, y);
   Feld[x][y] = target_element;
 
+  Changed[x][y] = TRUE;                /* no more changes in this frame */
+
   ResetGfxAnimation(x, y);
   ResetRandomAnimationValue(x, y);
 
@@ -5342,17 +5364,14 @@ static void ChangeElementNowExt(int x, int y, int target_element)
     RelocatePlayer(x, y, target_element);
 }
 
-static void ChangeElementNow(int x, int y, int element)
+static void ChangeElementNow(int x, int y, int element, int page)
 {
-  struct ElementChangeInfo *change = &element_info[element].change;
+  struct ElementChangeInfo *change = &element_info[element].change_page[page];
 
-  /* prevent CheckTriggeredElementChange() from looping */
-  Changing[x][y] = TRUE;
+  Changed[x][y] = TRUE;                /* no more changes in this frame */
 
   CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
 
-  Changing[x][y] = FALSE;
-
   if (change->explode)
   {
     Bang(x, y);
@@ -5431,9 +5450,9 @@ static void ChangeElementNow(int x, int y, int element)
 
          something_has_changed = TRUE;
 
-         /* for symmetry reasons, stop newly created border elements */
+         /* for symmetry reasons, freeze newly created border elements */
          if (ex != x || ey != y)
-           Stop[ex][ey] = TRUE;
+           Stop[ex][ey] = TRUE;        /* no more moving in this frame */
        }
       }
 
@@ -5449,42 +5468,21 @@ static void ChangeElementNow(int x, int y, int element)
   }
 }
 
-static void ChangeElement(int x, int y)
+static void ChangeElement(int x, int y, int page)
 {
-#if 1
   int element = MovingOrBlocked2Element(x, y);
-#else
-  int element = Feld[x][y];
-#endif
-  struct ElementChangeInfo *change = &element_info[element].change;
+  struct ElementChangeInfo *change = &element_info[element].change_page[page];
 
   if (ChangeDelay[x][y] == 0)          /* initialize element change */
   {
-#if 1
     ChangeDelay[x][y] = (    change->delay_fixed  * change->delay_frames +
                         RND(change->delay_random * change->delay_frames)) + 1;
-#else
-    ChangeDelay[x][y] = changing_element[element].change_delay + 1;
-
-    if (IS_CUSTOM_ELEMENT(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
-    {
-      int max_random_delay = element_info[element].change.delay_random;
-      int delay_frames = element_info[element].change.delay_frames;
-
-      ChangeDelay[x][y] += RND(max_random_delay * delay_frames);
-    }
-#endif
 
     ResetGfxAnimation(x, y);
     ResetRandomAnimationValue(x, y);
 
-#if 1
     if (change->pre_change_function)
       change->pre_change_function(x, y);
-#else
-    if (changing_element[element].pre_change_function)
-      changing_element[element].pre_change_function(x, y);
-#endif
   }
 
   ChangeDelay[x][y]--;
@@ -5496,20 +5494,11 @@ static void ChangeElement(int x, int y)
     if (IS_ANIMATED(graphic))
       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
 
-#if 1
     if (change->change_function)
       change->change_function(x, y);
-#else
-    if (changing_element[element].change_function)
-      changing_element[element].change_function(x, y);
-#endif
   }
   else                                 /* finish element change */
   {
-#if 0
-    int next_element = changing_element[element].next_element;
-#endif
-
     if (IS_MOVING(x, y))               /* never change a running system ;-) */
     {
       ChangeDelay[x][y] = 1;           /* try change after next move step */
@@ -5517,70 +5506,112 @@ static void ChangeElement(int x, int y)
       return;
     }
 
-#if 1
-    ChangeElementNow(x, y, element);
+    ChangeElementNow(x, y, element, page);
 
     if (change->post_change_function)
       change->post_change_function(x, y);
-#else
-    if (next_element != EL_UNDEFINED)
-      ChangeElementNow(x, y, next_element);
-    else
-      ChangeElementNow(x, y, element_info[element].change.target_element);
-
-    if (changing_element[element].post_change_function)
-      changing_element[element].post_change_function(x, y);
-#endif
   }
 }
 
 static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
                                           int trigger_event)
 {
-  int i, x, y;
+  int i, j, x, y;
 
   if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
     return FALSE;
 
-  for (i=0; i<MAX_NUM_ELEMENTS; i++)
+#if 0
+  /* prevent this function from running into a loop */
+  if (trigger_event == CE_OTHER_IS_CHANGING)
+    Changed[lx][ly] = TRUE;
+#endif
+
+  for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
   {
-    if (!CAN_CHANGE(i) || !HAS_CHANGE_EVENT(i, trigger_event) ||
-       element_info[i].change.trigger_element != trigger_element)
+    int element = EL_CUSTOM_START + i;
+
+#if 1
+    boolean change_element = FALSE;
+    int page;
+
+    if (!CAN_CHANGE(element) ||
+       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
       continue;
 
+    for (j=0; j < element_info[element].num_change_pages; j++)
+    {
+      if (element_info[element].change_page[j].trigger_element ==
+         trigger_element)
+      {
+       change_element = TRUE;
+       page = j;
+
+       break;
+      }
+    }
+
+    if (!change_element)
+      continue;
+
+#else
+    if (!CAN_CHANGE(element) ||
+       !HAS_ANY_CHANGE_EVENT(element, trigger_event) ||
+       element_info[element].change->trigger_element != trigger_element)
+      continue;
+#endif
+
     for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
     {
       if (x == lx && y == ly)  /* do not change trigger element itself */
        continue;
 
-      if (Changing[x][y])      /* do not change just changing elements */
+      if (Changed[x][y])       /* do not change already changed elements */
        continue;
 
-      if (Feld[x][y] == i)
+      if (Feld[x][y] == element)
       {
        ChangeDelay[x][y] = 1;
-       ChangeElement(x, y);
+       ChangeElement(x, y, page);
+
+#if 0
+       Changed[x][y] = TRUE;   /* prevent element from being changed again */
+#endif
       }
     }
   }
 
+#if 0
+  /* reset change prevention array */
+  for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
+    Changed[x][y] = FALSE;
+#endif
+
   return TRUE;
 }
 
-static boolean CheckElementChange(int x, int y, int element, int trigger_event)
+static boolean CheckElementChangeExt(int x, int y, int element,
+                                    int trigger_event, int page)
 {
-  if (!CAN_CHANGE(element) || !HAS_CHANGE_EVENT(element, trigger_event))
+  if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
     return FALSE;
 
   if (Feld[x][y] == EL_BLOCKED)
     Blocked2Moving(x, y, &x, &y);
 
   ChangeDelay[x][y] = 1;
-  ChangeElement(x, y);
+  ChangeElement(x, y, page);
 
   return TRUE;
 }
 
+static boolean CheckElementChange(int x, int y, int element, int trigger_event)
+{
+  int page = element_info[element].event_page_num[trigger_event];
+
+  return CheckElementChangeExt(x, y, element, trigger_event, page);
+}
+
 static void PlayerActions(struct PlayerInfo *player, byte player_action)
 {
   static byte stored_player_action[MAX_PLAYERS];
@@ -5633,13 +5664,7 @@ static void PlayerActions(struct PlayerInfo *player, byte player_action)
     CheckGravityMovement(player);
 
     if (player->MovPos == 0)
-    {
-#if 0
-      printf("Trying... Player frame reset\n");
-#endif
-
       InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
-    }
 
     if (player->MovPos == 0)   /* needed for tape.playing */
       player->is_moving = FALSE;
@@ -5774,6 +5799,8 @@ void GameActions()
 
   for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
   {
+    Changed[x][y] = FALSE;
+
     Stop[x][y] = FALSE;
     if (JustStopped[x][y] > 0)
       JustStopped[x][y]--;
@@ -5782,7 +5809,7 @@ void GameActions()
 
 #if 1
     /* reset finished pushing action (not done in ContinueMoving() to allow
-       continous pushing animation for elements without push delay) */
+       continous pushing animation for elements with zero push delay) */
     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
     {
       ResetGfxAnimation(x, y);
@@ -5850,7 +5877,7 @@ void GameActions()
     /* this may take place after moving, so 'element' may have changed */
     if (IS_CHANGING(x, y))
     {
-      ChangeElement(x, y);
+      ChangeElement(x, y, element_info[element].event_page_num[CE_DELAY]);
       element = Feld[x][y];
       graphic = el_act_dir2img(element, GfxAction[x][y], MovDir[x][y]);
     }
@@ -6515,6 +6542,9 @@ boolean MoveFigure(struct PlayerInfo *player, int dx, int dy)
 
     player->last_move_dir = player->MovDir;
     player->is_moving = TRUE;
+#if 1
+    player->snapped = FALSE;
+#endif
   }
   else
   {
@@ -6635,7 +6665,9 @@ void ScrollScreen(struct PlayerInfo *player, int mode)
 
 void TestIfPlayerTouchesCustomElement(int x, int y)
 {
+#if 0
   static boolean check_changing = FALSE;
+#endif
   static int xy[4][2] =
   {
     { 0, -1 },
@@ -6645,10 +6677,12 @@ void TestIfPlayerTouchesCustomElement(int x, int y)
   };
   int i;
 
+#if 0
   if (check_changing)  /* prevent this function from running into a loop */
     return;
 
   check_changing = TRUE;
+#endif
 
   for (i=0; i<4; i++)
   {
@@ -6672,12 +6706,16 @@ void TestIfPlayerTouchesCustomElement(int x, int y)
     }
   }
 
+#if 0
   check_changing = FALSE;
+#endif
 }
 
 void TestIfElementTouchesCustomElement(int x, int y)
 {
+#if 0
   static boolean check_changing = FALSE;
+#endif
   static int xy[4][2] =
   {
     { 0, -1 },
@@ -6686,13 +6724,16 @@ void TestIfElementTouchesCustomElement(int x, int y)
     { 0, +1 }
   };
   boolean change_center_element = FALSE;
+  int center_element_change_page = 0;
   int center_element = Feld[x][y];
-  int i;
+  int i, j;
 
+#if 0
   if (check_changing)  /* prevent this function from running into a loop */
     return;
 
   check_changing = TRUE;
+#endif
 
   for (i=0; i<4; i++)
   {
@@ -6707,19 +6748,51 @@ void TestIfElementTouchesCustomElement(int x, int y)
 
     /* check for change of center element (but change it only once) */
     if (IS_CUSTOM_ELEMENT(center_element) &&
-       border_element == element_info[center_element].change.trigger_element)
-      change_center_element = TRUE;
+       HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
+       !change_center_element)
+    {
+      for (j=0; j < element_info[center_element].num_change_pages; j++)
+      {
+       struct ElementChangeInfo *change =
+         &element_info[center_element].change_page[j];
+
+       if (change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
+           change->trigger_element == border_element)
+       {
+         change_center_element = TRUE;
+         center_element_change_page = j;
+
+         break;
+       }
+      }
+    }
 
     /* check for change of border element */
     if (IS_CUSTOM_ELEMENT(border_element) &&
-       center_element == element_info[border_element].change.trigger_element)
-      CheckElementChange(xx, yy, border_element, CE_OTHER_IS_TOUCHING);
+       HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
+    {
+      for (j=0; j < element_info[border_element].num_change_pages; j++)
+      {
+       struct ElementChangeInfo *change =
+         &element_info[border_element].change_page[j];
+
+       if (change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
+           change->trigger_element == center_element)
+       {
+         CheckElementChangeExt(xx,yy, border_element,CE_OTHER_IS_TOUCHING, j);
+         break;
+       }
+      }
+    }
   }
 
   if (change_center_element)
-    CheckElementChange(x, y, center_element, CE_OTHER_IS_TOUCHING);
+    CheckElementChangeExt(x, y, center_element, CE_OTHER_IS_TOUCHING,
+                         center_element_change_page);
 
+#if 0
   check_changing = FALSE;
+#endif
 }
 
 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
@@ -7407,6 +7480,10 @@ int DigField(struct PlayerInfo *player,
 
        PlaySoundLevelElementAction(x, y, element, ACTION_DIGGING);
 
+       CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED);
+
+       TestIfElementTouchesCustomElement(x, y);
+
        break;
       }
       else if (IS_COLLECTIBLE(element))
@@ -7467,6 +7544,10 @@ int DigField(struct PlayerInfo *player,
                             el2edimg(EL_KEY_1 + key_nr));
          redraw_mask |= REDRAW_DOOR_1;
        }
+       else if (element == EL_ENVELOPE)
+       {
+         ShowEnvelope();
+       }
        else if (IS_DROPPABLE(element)) /* can be collected and dropped */
        {
          int i;
@@ -7494,6 +7575,8 @@ int DigField(struct PlayerInfo *player,
 
        CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
 
+       TestIfElementTouchesCustomElement(x, y);
+
        break;
       }
       else if (IS_PUSHABLE(element))
@@ -7634,8 +7717,15 @@ boolean SnapField(struct PlayerInfo *player, int dx, int dy)
     {
       player->is_digging = FALSE;
       player->is_collecting = FALSE;
+#if 1
+      player->is_moving = FALSE;
+#endif
     }
 
+#if 0
+    printf("::: trying to snap...\n");
+#endif
+
     return FALSE;
   }
 
@@ -7644,12 +7734,25 @@ boolean SnapField(struct PlayerInfo *player, int dx, int dy)
 
   player->MovDir = snap_direction;
 
+#if 1
+  player->is_digging = FALSE;
+  player->is_collecting = FALSE;
+#if 1
+  player->is_moving = FALSE;
+#endif
+#endif
+
   if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
     return FALSE;
 
   player->snapped = TRUE;
+#if 1
   player->is_digging = FALSE;
   player->is_collecting = FALSE;
+#if 1
+  player->is_moving = FALSE;
+#endif
+#endif
 
   DrawLevelField(x, y);
   BackToFront();
@@ -7712,6 +7815,8 @@ boolean DropElement(struct PlayerInfo *player)
 
     CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
     CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
+
+    TestIfElementTouchesCustomElement(jx, jy);
   }
   else         /* player is dropping a dyna bomb */
   {
index c129a676540638525f22d3e1c38facde79b61573..93aceacd47e332b504895acb2042bc0fb8a3ac70 100644 (file)
@@ -554,22 +554,24 @@ void InitElementGraphicInfo()
   {
     for (act=0; act<NUM_ACTIONS; act++)
     {
-      if (graphic_info[element_info[i].graphic[act]].bitmap == NULL)
+      int graphic;
+
+      graphic = element_info[i].graphic[act];
+      if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
        element_info[i].graphic[act] = -1;
 
-      if (graphic_info[element_info[i].crumbled[act]].bitmap == NULL)
+      graphic = element_info[i].crumbled[act];
+      if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
        element_info[i].crumbled[act] = -1;
 
       for (dir=0; dir<NUM_DIRECTIONS; dir++)
       {
-       int graphic;
-
        graphic = element_info[i].direction_graphic[act][dir];
-       if (graphic_info[graphic].bitmap == NULL)
+       if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
          element_info[i].direction_graphic[act][dir] = -1;
 
        graphic = element_info[i].direction_crumbled[act][dir];
-       if (graphic_info[graphic].bitmap == NULL)
+       if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
          element_info[i].direction_crumbled[act][dir] = -1;
       }
     }
@@ -773,6 +775,7 @@ static void set_graphic_parameters(int graphic, char **parameter_raw)
   graphic_info[graphic].offset_y = 0;  /* ... will be corrected later */
   graphic_info[graphic].crumbled_like = -1;    /* do not use clone element */
   graphic_info[graphic].diggable_like = -1;    /* do not use clone element */
+  graphic_info[graphic].border_size = TILEX / 8;  /* "CRUMBLED" border size */
 
   /* optional x and y tile position of animation frame sequence */
   if (parameter[GFX_ARG_XPOS] != ARG_UNDEFINED_VALUE)
@@ -862,6 +865,10 @@ static void set_graphic_parameters(int graphic, char **parameter_raw)
   if (parameter[GFX_ARG_DIGGABLE_LIKE] != ARG_UNDEFINED_VALUE)
     graphic_info[graphic].diggable_like = parameter[GFX_ARG_DIGGABLE_LIKE];
 
+  /* optional border size for "crumbling" diggable graphics */
+  if (parameter[GFX_ARG_BORDER_SIZE] != ARG_UNDEFINED_VALUE)
+    graphic_info[graphic].border_size = parameter[GFX_ARG_BORDER_SIZE];
+
   /* this is only used for toon animations */
   graphic_info[graphic].step_offset = parameter[GFX_ARG_STEP_OFFSET];
   graphic_info[graphic].step_delay  = parameter[GFX_ARG_STEP_DELAY];
@@ -869,6 +876,9 @@ static void set_graphic_parameters(int graphic, char **parameter_raw)
   /* this is only used for drawing font characters */
   graphic_info[graphic].draw_x = parameter[GFX_ARG_DRAW_XOFFSET];
   graphic_info[graphic].draw_y = parameter[GFX_ARG_DRAW_YOFFSET];
+
+  /* this is only used for drawing envelope graphics */
+  graphic_info[graphic].draw_masked = parameter[GFX_ARG_DRAW_MASKED];
 }
 
 static void InitGraphicInfo()
@@ -892,6 +902,10 @@ static void InitGraphicInfo()
 
   graphic_info = checked_calloc(num_images * sizeof(struct GraphicInfo));
 
+#if 0
+  printf("::: graphic_info: %d entries\n", num_images);
+#endif
+
 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
   if (clipmasks_initialized)
   {
@@ -2829,6 +2843,12 @@ void InitElementPropertiesEngine(int engine_version)
             element_info[i].token_name,
             element_info[i].crumbled[ACTION_DEFAULT]);
 #endif
+
+    /* ---------- CAN_CHANGE ----------------------------------------------- */
+    SET_PROPERTY(i, EP_CAN_CHANGE, FALSE);     /* default: cannot change */
+    for (j=0; j < element_info[i].num_change_pages; j++)
+      if (element_info[i].change_page[j].can_change)
+       SET_PROPERTY(i, EP_CAN_CHANGE, TRUE);
   }
 
 #if 0
index d2af524ac4353ceb8505c59351ea8e9834ca17e0..dd9d28838273d6a98b8960b71d58705d950b9349 100644 (file)
@@ -22,6 +22,7 @@
 /* values for DrawGadget() */
 #define DG_UNPRESSED           0
 #define DG_PRESSED             1
+
 #define DG_BUFFERED            0
 #define DG_DIRECT              1
 
@@ -104,6 +105,67 @@ static struct GadgetInfo *getGadgetInfoFromMousePosition(int mx, int my)
   return NULL;
 }
 
+static void setTextAreaCursorExt(struct GadgetInfo *gi, boolean set_cursor_pos)
+{
+  char *text = gi->textarea.value;
+  int area_xsize = gi->textarea.xsize;
+  int area_ysize = gi->textarea.ysize;
+  int cursor_position = gi->textarea.cursor_position;
+  int cursor_x = gi->textarea.cursor_x;
+  int cursor_y = gi->textarea.cursor_y;
+  int pos = 0;
+  int x = 0;
+  int y = 0;
+
+  while (*text)
+  {
+    if (set_cursor_pos)                /* x/y => position */
+    {
+      if (y == cursor_y && (x == cursor_x || (x < cursor_x && *text == '\n')))
+       break;
+    }
+    else                       /* position => x/y */
+    {
+      if (pos == cursor_position)
+       break;
+    }
+
+    if (x + 1 >= area_xsize || *text == '\n')
+    {
+      if (y + 1 >= area_ysize)
+       break;
+
+      x = 0;
+      y++;
+    }
+    else
+      x++;
+
+    text++;
+    pos++;
+  }
+
+  gi->textarea.cursor_x = x;
+  gi->textarea.cursor_y = y;
+  gi->textarea.cursor_x_preferred = x;
+  gi->textarea.cursor_position = pos;
+}
+
+static void setTextAreaCursorXY(struct GadgetInfo *gi, int x, int y)
+{
+  gi->textarea.cursor_x = x;
+  gi->textarea.cursor_y = y;
+
+  setTextAreaCursorExt(gi, TRUE);
+}
+
+static void setTextAreaCursorPosition(struct GadgetInfo *gi, int pos)
+{
+  gi->textarea.cursor_position = pos;
+
+  setTextAreaCursorExt(gi, FALSE);
+}
+
 static void default_callback_info(void *ptr)
 {
   return;
@@ -172,8 +234,8 @@ static void DrawGadget(struct GadgetInfo *gi, boolean pressed, boolean direct)
       }
       break;
 
-    case GD_TYPE_TEXTINPUT_ALPHANUMERIC:
-    case GD_TYPE_TEXTINPUT_NUMERIC:
+    case GD_TYPE_TEXT_INPUT_ALPHANUMERIC:
+    case GD_TYPE_TEXT_INPUT_NUMERIC:
       {
        int i;
        char cursor_letter;
@@ -189,7 +251,7 @@ static void DrawGadget(struct GadgetInfo *gi, boolean pressed, boolean direct)
                               border_x, gi->height, gi->x, gi->y);
 
        /* middle part of gadget */
-       for (i=0; i < gi->text.size + 1; i++)
+       for (i=0; i < gi->textinput.size + 1; i++)
          BlitBitmapOnBackground(gd->bitmap, drawto, gd->x + border_x, gd->y,
                                 font_width, gi->height,
                                 gi->x + border_x + i * font_width, gi->y);
@@ -201,7 +263,7 @@ static void DrawGadget(struct GadgetInfo *gi, boolean pressed, boolean direct)
                               gi->x + gi->width - border_x, gi->y);
 
        /* set text value */
-       strcpy(text, gi->text.value);
+       strcpy(text, gi->textinput.value);
        strcat(text, " ");
 
        /* gadget text value */
@@ -209,19 +271,111 @@ static void DrawGadget(struct GadgetInfo *gi, boolean pressed, boolean direct)
                    gi->x + border_x, gi->y + border_y, text,
                    font_nr, BLIT_MASKED);
 
-       cursor_letter = gi->text.value[gi->text.cursor_position];
+       cursor_letter = gi->textinput.value[gi->textinput.cursor_position];
        cursor_string[0] = (cursor_letter != '\0' ? cursor_letter : ' ');
        cursor_string[1] = '\0';
 
        /* draw cursor, if active */
        if (pressed)
          DrawTextExt(drawto,
-                     gi->x + border_x + gi->text.cursor_position * font_width,
+                     gi->x + border_x +
+                     gi->textinput.cursor_position * font_width,
                      gi->y + border_y, cursor_string,
                      font_nr, BLIT_INVERSE);
       }
       break;
 
+    case GD_TYPE_TEXT_AREA:
+      {
+       int i;
+       char cursor_letter;
+       char cursor_string[2];
+       int font_nr = (pressed ? gi->font_active : gi->font);
+       int font_width = getFontWidth(font_nr);
+       int font_height = getFontHeight(font_nr);
+       int border_x = gi->border.xsize;
+       int border_y = gi->border.ysize;
+       int gd_height = 2 * border_y + font_height;
+
+       /* top left part of gadget border */
+       BlitBitmapOnBackground(gd->bitmap, drawto, gd->x, gd->y,
+                              border_x, border_y, gi->x, gi->y);
+
+       /* top middle part of gadget border */
+       for (i=0; i < gi->textarea.xsize; i++)
+         BlitBitmapOnBackground(gd->bitmap, drawto, gd->x + border_x, gd->y,
+                                font_width, border_y,
+                                gi->x + border_x + i * font_width, gi->y);
+
+       /* top right part of gadget border */
+       BlitBitmapOnBackground(gd->bitmap, drawto,
+                              gd->x + gi->border.width - border_x, gd->y,
+                              border_x, border_y,
+                              gi->x + gi->width - border_x, gi->y);
+
+       /* left and right part of gadget border for each row */
+       for (i=0; i < gi->textarea.ysize; i++)
+       {
+         BlitBitmapOnBackground(gd->bitmap, drawto, gd->x, gd->y + border_y,
+                                border_x, font_height,
+                                gi->x, gi->y + border_y + i * font_height);
+         BlitBitmapOnBackground(gd->bitmap, drawto,
+                                gd->x + gi->border.width - border_x,
+                                gd->y + border_y,
+                                border_x, font_height,
+                                gi->x + gi->width - border_x,
+                                gi->y + border_y + i * font_height);
+       }
+
+       /* bottom left part of gadget border */
+       BlitBitmapOnBackground(gd->bitmap, drawto,
+                              gd->x, gd->y + gd_height - border_y,
+                              border_x, border_y,
+                              gi->x, gi->y + gi->height - border_y);
+
+       /* bottom middle part of gadget border */
+       for (i=0; i < gi->textarea.xsize; i++)
+         BlitBitmapOnBackground(gd->bitmap, drawto,
+                                gd->x + border_x,
+                                gd->y + gd_height - border_y,
+                                font_width, border_y,
+                                gi->x + border_x + i * font_width,
+                                gi->y + gi->height - border_y);
+
+       /* bottom right part of gadget border */
+       BlitBitmapOnBackground(gd->bitmap, drawto,
+                              gd->x + gi->border.width - border_x,
+                              gd->y + gd_height - border_y,
+                              border_x, border_y,
+                              gi->x + gi->width - border_x,
+                              gi->y + gi->height - border_y);
+
+       ClearRectangleOnBackground(drawto,
+                                  gi->x + border_x,
+                                  gi->y + border_y,
+                                  gi->width - 2 * border_x,
+                                  gi->height - 2 * border_y);
+
+       /* gadget text value */
+       DrawTextToTextArea(gi->x + border_x, gi->y + border_y,
+                          gi->textarea.value, font_nr,
+                          gi->textarea.xsize, gi->textarea.ysize,
+                          BLIT_ON_BACKGROUND);
+
+       cursor_letter = gi->textarea.value[gi->textarea.cursor_position];
+       cursor_string[0] = (cursor_letter != '\0' ? cursor_letter : ' ');
+       cursor_string[1] = '\0';
+
+       /* draw cursor, if active */
+       if (pressed)
+         DrawTextExt(drawto,
+                     gi->x + border_x + gi->textarea.cursor_x * font_width,
+                     gi->y + border_y + gi->textarea.cursor_y * font_height,
+                     cursor_string,
+                     font_nr, BLIT_INVERSE);
+      }
+      break;
+
     case GD_TYPE_SELECTBOX:
       {
        int i;
@@ -366,7 +520,10 @@ static void DrawGadget(struct GadgetInfo *gi, boolean pressed, boolean direct)
          /* selectbox text values */
          for (i=0; i < gi->selectbox.num_values; i++)
          {
-           int mask_mode;
+           int mask_mode = BLIT_MASKED;
+
+           strncpy(text, gi->selectbox.options[i].text, gi->selectbox.size);
+           text[gi->selectbox.size] = '\0';
 
            if (i == gi->selectbox.current_index)
            {
@@ -376,17 +533,11 @@ static void DrawGadget(struct GadgetInfo *gi, boolean pressed, boolean direct)
                            gi->selectbox.width - 2 * border_x, font_height,
                            gi->selectbox.inverse_color);
 
-             strncpy(text, gi->selectbox.options[i].text, gi->selectbox.size);
-             text[1 + gi->selectbox.size] = '\0';
-
-             mask_mode = BLIT_INVERSE;
-           }
-           else
-           {
-             strncpy(text, gi->selectbox.options[i].text, gi->selectbox.size);
+             /* prevent use of cursor graphic by drawing at least two chars */
+             strcat(text, "  ");
              text[gi->selectbox.size] = '\0';
 
-             mask_mode = BLIT_MASKED;
+             mask_mode = BLIT_INVERSE;
            }
 
            DrawTextExt(drawto,
@@ -590,6 +741,12 @@ static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap)
        gi->active = (boolean)va_arg(ap, int);
        break;
 
+      case GDI_DIRECT_DRAW:
+       /* take care here: "boolean" is typedef'ed as "unsigned char",
+          which gets promoted to "int" */
+       gi->direct_draw = (boolean)va_arg(ap, int);
+       break;
+
       case GDI_CHECKED:
        /* take care here: "boolean" is typedef'ed as "unsigned char",
           which gets promoted to "int" */
@@ -601,26 +758,26 @@ static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap)
        break;
 
       case GDI_NUMBER_VALUE:
-       gi->text.number_value = va_arg(ap, long);
-       sprintf(gi->text.value, "%d", gi->text.number_value);
-       gi->text.cursor_position = strlen(gi->text.value);
+       gi->textinput.number_value = va_arg(ap, long);
+       sprintf(gi->textinput.value, "%d", gi->textinput.number_value);
+       gi->textinput.cursor_position = strlen(gi->textinput.value);
        break;
 
       case GDI_NUMBER_MIN:
-       gi->text.number_min = va_arg(ap, long);
-       if (gi->text.number_value < gi->text.number_min)
+       gi->textinput.number_min = va_arg(ap, long);
+       if (gi->textinput.number_value < gi->textinput.number_min)
        {
-         gi->text.number_value = gi->text.number_min;
-         sprintf(gi->text.value, "%d", gi->text.number_value);
+         gi->textinput.number_value = gi->textinput.number_min;
+         sprintf(gi->textinput.value, "%d", gi->textinput.number_value);
        }
        break;
 
       case GDI_NUMBER_MAX:
-       gi->text.number_max = va_arg(ap, long);
-       if (gi->text.number_value > gi->text.number_max)
+       gi->textinput.number_max = va_arg(ap, long);
+       if (gi->textinput.number_value > gi->textinput.number_max)
        {
-         gi->text.number_value = gi->text.number_max;
-         sprintf(gi->text.value, "%d", gi->text.number_value);
+         gi->textinput.number_value = gi->textinput.number_max;
+         sprintf(gi->textinput.value, "%d", gi->textinput.number_value);
        }
        break;
 
@@ -628,15 +785,16 @@ static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap)
        {
          int max_textsize = MAX_GADGET_TEXTSIZE;
 
-         if (gi->text.size)
-           max_textsize = MIN(gi->text.size, MAX_GADGET_TEXTSIZE - 1);
+         if (gi->textinput.size)
+           max_textsize = MIN(gi->textinput.size, MAX_GADGET_TEXTSIZE - 1);
 
-         strncpy(gi->text.value, va_arg(ap, char *), max_textsize);
-         gi->text.value[max_textsize] = '\0';
-         gi->text.cursor_position = strlen(gi->text.value);
+         strncpy(gi->textinput.value, va_arg(ap, char *), max_textsize);
+         gi->textinput.value[max_textsize] = '\0';
+         gi->textinput.cursor_position = strlen(gi->textinput.value);
 
-         /* same tag also used for textbutton definition */
-         strcpy(gi->textbutton.value, gi->text.value);
+         /* same tag also used for other gadget definitions */
+         strcpy(gi->textbutton.value, gi->textinput.value);
+         strcpy(gi->textarea.value, gi->textinput.value);
        }
        break;
 
@@ -645,13 +803,13 @@ static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap)
          int tag_value = va_arg(ap, int);
          int max_textsize = MIN(tag_value, MAX_GADGET_TEXTSIZE - 1);
 
-         gi->text.size = max_textsize;
-         gi->text.value[max_textsize] = '\0';
+         gi->textinput.size = max_textsize;
+         gi->textinput.value[max_textsize] = '\0';
 
-         /* same tag also used for textbutton and selectbox definition */
-         strcpy(gi->textbutton.value, gi->text.value);
-         gi->textbutton.size = gi->text.size;
-         gi->selectbox.size = gi->text.size;
+         /* same tag also used for other gadget definitions */
+         strcpy(gi->textbutton.value, gi->textinput.value);
+         gi->textbutton.size = gi->textinput.size;
+         gi->selectbox.size = gi->textinput.size;
        }
        break;
 
@@ -752,6 +910,17 @@ static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap)
          gi->drawing.item_xsize = gi->width / gi->drawing.area_xsize;
          gi->drawing.item_ysize = gi->height / gi->drawing.area_ysize;
        }
+
+       /* same tag also used for other gadget definitions */
+       gi->textarea.xsize = gi->drawing.area_xsize;
+       gi->textarea.ysize = gi->drawing.area_ysize;
+
+       if (gi->type & GD_TYPE_TEXT_AREA)       /* force recalculation */
+       {
+         gi->width = 0;
+         gi->height = 0;
+       }
+
        break;
 
       case GDI_ITEM_SIZE:
@@ -808,7 +977,7 @@ static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap)
 
   /* adjust gadget values in relation to other gadget values */
 
-  if (gi->type & GD_TYPE_TEXTINPUT)
+  if (gi->type & GD_TYPE_TEXT_INPUT)
   {
     int font_nr = gi->font_active;
     int font_width = getFontWidth(font_nr);
@@ -816,7 +985,7 @@ static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap)
     int border_xsize = gi->border.xsize;
     int border_ysize = gi->border.ysize;
 
-    gi->width  = 2 * border_xsize + (gi->text.size + 1) * font_width;
+    gi->width  = 2 * border_xsize + (gi->textinput.size + 1) * font_width;
     gi->height = 2 * border_ysize + font_height;
   }
 
@@ -832,7 +1001,7 @@ static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap)
     Bitmap *src_bitmap;
     int src_x, src_y;
 
-    gi->width  = 2 * border_xsize + gi->text.size * font_width + button_size;
+    gi->width  = 2 * border_xsize + gi->textinput.size*font_width +button_size;
     gi->height = 2 * border_ysize + font_height;
 
     if (gi->selectbox.options == NULL)
@@ -863,9 +1032,9 @@ static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap)
     gi->selectbox.open = FALSE;
   }
 
-  if (gi->type & GD_TYPE_TEXTINPUT_NUMERIC)
+  if (gi->type & GD_TYPE_TEXT_INPUT_NUMERIC)
   {
-    struct GadgetTextInput *text = &gi->text;
+    struct GadgetTextInput *text = &gi->textinput;
     int value = text->number_value;
 
     text->number_value = (value < text->number_min ? text->number_min :
@@ -907,6 +1076,26 @@ static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap)
     if (gs->item_position == gs->items_max - gs->items_visible)
       gs->position = gs->position_max;
   }
+
+  if (gi->type & GD_TYPE_TEXT_AREA)
+  {
+    int font_nr = gi->font_active;
+    int font_width = getFontWidth(font_nr);
+    int font_height = getFontHeight(font_nr);
+    int border_xsize = gi->border.xsize;
+    int border_ysize = gi->border.ysize;
+
+    if (gi->width == 0 || gi->height == 0)
+    {
+      gi->width  = 2 * border_xsize + gi->textarea.xsize * font_width;
+      gi->height = 2 * border_ysize + gi->textarea.ysize * font_height;
+    }
+    else
+    {
+      gi->textarea.xsize = (gi->width  - 2 * border_xsize) / font_width;
+      gi->textarea.ysize = (gi->height - 2 * border_ysize) / font_height;
+    }
+  }
 }
 
 void ModifyGadget(struct GadgetInfo *gi, int first_tag, ...)
@@ -923,7 +1112,7 @@ void ModifyGadget(struct GadgetInfo *gi, int first_tag, ...)
 void RedrawGadget(struct GadgetInfo *gi)
 {
   if (gi->mapped)
-    DrawGadget(gi, gi->state, DG_DIRECT);
+    DrawGadget(gi, gi->state, gi->direct_draw);
 }
 
 struct GadgetInfo *CreateGadget(int first_tag, ...)
@@ -936,6 +1125,8 @@ struct GadgetInfo *CreateGadget(int first_tag, ...)
   new_gadget->callback_info = default_callback_info;
   new_gadget->callback_action = default_callback_action;
   new_gadget->active = TRUE;
+  new_gadget->direct_draw = TRUE;
+
   new_gadget->next = NULL;
 
   va_start(ap, first_tag);
@@ -975,22 +1166,22 @@ void FreeGadget(struct GadgetInfo *gi)
 
 static void CheckRangeOfNumericInputGadget(struct GadgetInfo *gi)
 {
-  if (gi->type != GD_TYPE_TEXTINPUT_NUMERIC)
+  if (gi->type != GD_TYPE_TEXT_INPUT_NUMERIC)
     return;
 
-  gi->text.number_value = atoi(gi->text.value);
+  gi->textinput.number_value = atoi(gi->textinput.value);
 
-  if (gi->text.number_value < gi->text.number_min)
-    gi->text.number_value = gi->text.number_min;
-  if (gi->text.number_value > gi->text.number_max)
-    gi->text.number_value = gi->text.number_max;
+  if (gi->textinput.number_value < gi->textinput.number_min)
+    gi->textinput.number_value = gi->textinput.number_min;
+  if (gi->textinput.number_value > gi->textinput.number_max)
+    gi->textinput.number_value = gi->textinput.number_max;
 
-  sprintf(gi->text.value, "%d", gi->text.number_value);
+  sprintf(gi->textinput.value, "%d", gi->textinput.number_value);
 
-  if (gi->text.cursor_position < 0)
-    gi->text.cursor_position = 0;
-  else if (gi->text.cursor_position > strlen(gi->text.value))
-    gi->text.cursor_position = strlen(gi->text.value);
+  if (gi->textinput.cursor_position < 0)
+    gi->textinput.cursor_position = 0;
+  else if (gi->textinput.cursor_position > strlen(gi->textinput.value))
+    gi->textinput.cursor_position = strlen(gi->textinput.value);
 }
 
 /* global pointer to gadget actually in use (when mouse button pressed) */
@@ -1076,19 +1267,26 @@ void RemapAllGadgets()
   MultiMapGadgets(MULTIMAP_ALL | MULTIMAP_REMAP);
 }
 
-boolean anyTextInputGadgetActive()
+static boolean anyTextInputGadgetActive()
 {
-  return (last_gi && (last_gi->type & GD_TYPE_TEXTINPUT) && last_gi->mapped);
+  return (last_gi && (last_gi->type & GD_TYPE_TEXT_INPUT) && last_gi->mapped);
 }
 
-boolean anySelectboxGadgetActive()
+static boolean anyTextAreaGadgetActive()
+{
+  return (last_gi && (last_gi->type & GD_TYPE_TEXT_AREA) && last_gi->mapped);
+}
+
+static boolean anySelectboxGadgetActive()
 {
   return (last_gi && (last_gi->type & GD_TYPE_SELECTBOX) && last_gi->mapped);
 }
 
 boolean anyTextGadgetActive()
 {
-  return (anyTextInputGadgetActive() || anySelectboxGadgetActive());
+  return (anyTextInputGadgetActive() ||
+         anyTextAreaGadgetActive() ||
+         anySelectboxGadgetActive());
 }
 
 void ClickOnGadget(struct GadgetInfo *gi, int button)
@@ -1151,76 +1349,20 @@ void HandleGadgets(int mx, int my, int button)
   last_mx = mx;
   last_my = my;
 
-  /* special treatment for text and number input gadgets */
-  if (anyTextInputGadgetActive() && button != 0 && !motion_status)
+  /* if mouse button pressed outside text or selectbox gadget, deactivate it */
+  if (anyTextGadgetActive() &&
+      button != 0 && !motion_status && new_gi != last_gi)
   {
-    struct GadgetInfo *gi = last_gi;
+    CheckRangeOfNumericInputGadget(last_gi);   /* in case of numeric gadget */
 
-    if (new_gi == last_gi)
-    {
-      int old_cursor_position = gi->text.cursor_position;
+    DrawGadget(last_gi, DG_UNPRESSED, last_gi->direct_draw);
 
-      /* if mouse button pressed inside activated text gadget, set cursor */
-      gi->text.cursor_position =
-       (mx - gi->x - gi->border.xsize) / getFontWidth(gi->font);
+    last_gi->event.type = GD_EVENT_TEXT_LEAVING;
 
-      if (gi->text.cursor_position < 0)
-       gi->text.cursor_position = 0;
-      else if (gi->text.cursor_position > strlen(gi->text.value))
-       gi->text.cursor_position = strlen(gi->text.value);
+    if (last_gi->event_mask & GD_EVENT_TEXT_LEAVING)
+      last_gi->callback_action(last_gi);
 
-      if (gi->text.cursor_position != old_cursor_position)
-       DrawGadget(gi, DG_PRESSED, DG_DIRECT);
-    }
-    else
-    {
-      /* if mouse button pressed outside text input gadget, deactivate it */
-      CheckRangeOfNumericInputGadget(gi);
-      DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
-
-      gi->event.type = GD_EVENT_TEXT_LEAVING;
-
-      if (gi->event_mask & GD_EVENT_TEXT_LEAVING)
-       gi->callback_action(gi);
-
-      last_gi = NULL;
-    }
-  }
-
-  /* special treatment for selectbox gadgets */
-  if (anySelectboxGadgetActive() && button != 0 && !motion_status)
-  {
-    struct GadgetInfo *gi = last_gi;
-
-    if (new_gi == last_gi)
-    {
-      int old_index = gi->selectbox.current_index;
-
-      /* if mouse button pressed inside activated selectbox, select value */
-      if (my >= gi->selectbox.y && my < gi->selectbox.y + gi->selectbox.height)
-       gi->selectbox.current_index =
-         (my - gi->selectbox.y - gi->border.ysize) / getFontHeight(gi->font);
-
-      if (gi->selectbox.current_index < 0)
-       gi->selectbox.current_index = 0;
-      else if (gi->selectbox.current_index > gi->selectbox.num_values - 1)
-       gi->selectbox.current_index = gi->selectbox.num_values - 1;
-
-      if (gi->selectbox.current_index != old_index)
-       DrawGadget(gi, DG_PRESSED, DG_DIRECT);
-    }
-    else
-    {
-      /* if mouse button pressed outside selectbox gadget, deactivate it */
-      DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
-
-      gi->event.type = GD_EVENT_TEXT_LEAVING;
-
-      if (gi->event_mask & GD_EVENT_TEXT_LEAVING)
-       gi->callback_action(gi);
-
-      last_gi = NULL;
-    }
+    last_gi = NULL;
   }
 
   gadget_pressed =
@@ -1269,11 +1411,12 @@ void HandleGadgets(int mx, int my, int button)
   /* if mouse button released, no gadget needs to be handled anymore */
   if (gadget_released)
   {
-    if ((last_gi->type & GD_TYPE_SELECTBOX) &&
+    if (last_gi->type & GD_TYPE_SELECTBOX &&
        (gadget_released_inside_select_line ||
-        gadget_released_off_borders))              /* selectbox stays open */
+        gadget_released_off_borders))               /* selectbox stays open */
       gi->selectbox.stay_open = TRUE;
-    else if (!(last_gi->type & GD_TYPE_TEXTINPUT))  /* text input stays open */
+    else if (!(last_gi->type & GD_TYPE_TEXT_INPUT ||
+              last_gi->type & GD_TYPE_TEXT_AREA))  /* text input stays open */
       last_gi = NULL;
   }
 
@@ -1297,6 +1440,36 @@ void HandleGadgets(int mx, int my, int button)
       if (last_x != gi->event.x || last_y != gi->event.y)
        changed_position = TRUE;
     }
+    else if (gi->type & GD_TYPE_TEXT_INPUT && button != 0 && !motion_status)
+    {
+      int old_cursor_position = gi->textinput.cursor_position;
+
+      /* if mouse button pressed inside activated text gadget, set cursor */
+      gi->textinput.cursor_position =
+       (mx - gi->x - gi->border.xsize) / getFontWidth(gi->font);
+
+      if (gi->textinput.cursor_position < 0)
+       gi->textinput.cursor_position = 0;
+      else if (gi->textinput.cursor_position > strlen(gi->textinput.value))
+       gi->textinput.cursor_position = strlen(gi->textinput.value);
+
+      if (gi->textinput.cursor_position != old_cursor_position)
+       DrawGadget(gi, DG_PRESSED, gi->direct_draw);
+    }
+    else if (gi->type & GD_TYPE_TEXT_AREA && button != 0 && !motion_status)
+    {
+      int old_cursor_position = gi->textarea.cursor_position;
+      int x = (mx - gi->x - gi->border.xsize) / getFontWidth(gi->font);
+      int y = (my - gi->y - gi->border.ysize) / getFontHeight(gi->font);
+
+      x = (x < 0 ? 0 : x >= gi->textarea.xsize ? gi->textarea.xsize - 1 : x);
+      y = (y < 0 ? 0 : y >= gi->textarea.ysize ? gi->textarea.ysize - 1 : y);
+
+      setTextAreaCursorXY(gi, x, y);
+
+      if (gi->textarea.cursor_position != old_cursor_position)
+       DrawGadget(gi, DG_PRESSED, gi->direct_draw);
+    }
     else if (gi->type & GD_TYPE_SELECTBOX)
     {
       int old_index = gi->selectbox.current_index;
@@ -1312,7 +1485,7 @@ void HandleGadgets(int mx, int my, int button)
        gi->selectbox.current_index = gi->selectbox.num_values - 1;
 
       if (gi->selectbox.current_index != old_index)
-       DrawGadget(gi, DG_PRESSED, DG_DIRECT);
+       DrawGadget(gi, DG_PRESSED, gi->direct_draw);
     }
   }
 
@@ -1329,15 +1502,6 @@ void HandleGadgets(int mx, int my, int button)
     {
       last_info_gi->event.type = GD_EVENT_INFO_LEAVING;
       last_info_gi->callback_info(last_info_gi);
-
-#if 0
-      default_callback_info(NULL);
-
-      printf("It seems that we are leaving gadget [%s]!\n",
-            (last_info_gi != NULL &&
-             last_info_gi->info_text != NULL ?
-             last_info_gi->info_text : ""));
-#endif
     }
 
     last_info_gi = new_gi;
@@ -1361,7 +1525,7 @@ void HandleGadgets(int mx, int my, int button)
            rgi != gi)
        {
          rgi->checked = FALSE;
-         DrawGadget(rgi, DG_UNPRESSED, DG_DIRECT);
+         DrawGadget(rgi, DG_UNPRESSED, rgi->direct_draw);
        }
 
        rgi = rgi->next;
@@ -1431,7 +1595,7 @@ void HandleGadgets(int mx, int my, int button)
       }
     }
 
-    DrawGadget(gi, DG_PRESSED, DG_DIRECT);
+    DrawGadget(gi, DG_PRESSED, gi->direct_draw);
 
     gi->state = GD_BUTTON_PRESSED;
     gi->event.type = GD_EVENT_PRESSED;
@@ -1459,9 +1623,9 @@ void HandleGadgets(int mx, int my, int button)
     if (gi->type & GD_TYPE_BUTTON)
     {
       if (gadget_moving_inside && gi->state == GD_BUTTON_UNPRESSED)
-       DrawGadget(gi, DG_PRESSED, DG_DIRECT);
+       DrawGadget(gi, DG_PRESSED, gi->direct_draw);
       else if (gadget_moving_off_borders && gi->state == GD_BUTTON_PRESSED)
-       DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
+       DrawGadget(gi, DG_UNPRESSED, gi->direct_draw);
     }
     else if (gi->type & GD_TYPE_SELECTBOX)
     {
@@ -1478,7 +1642,7 @@ void HandleGadgets(int mx, int my, int button)
        gi->selectbox.current_index = gi->selectbox.num_values - 1;
 
       if (gi->selectbox.current_index != old_index)
-       DrawGadget(gi, DG_PRESSED, DG_DIRECT);
+       DrawGadget(gi, DG_PRESSED, gi->direct_draw);
     }
     else if (gi->type & GD_TYPE_SCROLLBAR)
     {
@@ -1506,7 +1670,7 @@ void HandleGadgets(int mx, int my, int button)
        changed_position = TRUE;
       }
 
-      DrawGadget(gi, DG_PRESSED, DG_DIRECT);
+      DrawGadget(gi, DG_PRESSED, gi->direct_draw);
     }
 
     gi->state = (gadget_moving_inside || gi->type & GD_TYPE_SCROLLBAR ?
@@ -1533,8 +1697,9 @@ void HandleGadgets(int mx, int my, int button)
     }
 
     if (deactivate_gadget &&
-       !(gi->type & GD_TYPE_TEXTINPUT))            /* text input stays open */
-      DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
+       !(gi->type & GD_TYPE_TEXT_INPUT ||
+         gi->type & GD_TYPE_TEXT_AREA))            /* text input stays open */
+      DrawGadget(gi, DG_UNPRESSED, gi->direct_draw);
 
     gi->state = GD_BUTTON_UNPRESSED;
     gi->event.type = GD_EVENT_RELEASED;
@@ -1546,7 +1711,7 @@ void HandleGadgets(int mx, int my, int button)
   if (gadget_released_off_borders)
   {
     if (gi->type & GD_TYPE_SCROLLBAR)
-      DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
+      DrawGadget(gi, DG_UNPRESSED, gi->direct_draw);
 
     gi->event.type = GD_EVENT_RELEASED;
 
@@ -1560,71 +1725,161 @@ void HandleGadgets(int mx, int my, int button)
     new_gi->state = GD_BUTTON_UNPRESSED;
 }
 
+static void insertCharIntoTextArea(struct GadgetInfo *gi, char c)
+{
+  char text[MAX_GADGET_TEXTSIZE];
+  int cursor_position = gi->textarea.cursor_position;
+
+  if (strlen(gi->textarea.value) == MAX_GADGET_TEXTSIZE) /* no space left */
+    return;
+
+  strcpy(text, gi->textarea.value);
+  strcpy(&gi->textarea.value[cursor_position + 1], &text[cursor_position]);
+  gi->textarea.value[cursor_position] = c;
+
+  setTextAreaCursorPosition(gi, gi->textarea.cursor_position + 1);
+}
+
 void HandleGadgetsKeyInput(Key key)
 {
   struct GadgetInfo *gi = last_gi;
 
   if (gi == NULL || !gi->mapped ||
-      !((gi->type & GD_TYPE_TEXTINPUT) || (gi->type & GD_TYPE_SELECTBOX)))
+      !(gi->type & GD_TYPE_TEXT_INPUT ||
+       gi->type & GD_TYPE_TEXT_AREA ||
+       gi->type & GD_TYPE_SELECTBOX))
     return;
 
   if (key == KSYM_Return)      /* valid for both text input and selectbox */
   {
-    if (gi->type & GD_TYPE_TEXTINPUT)
+    if (gi->type & GD_TYPE_TEXT_INPUT)
       CheckRangeOfNumericInputGadget(gi);
     else if (gi->type & GD_TYPE_SELECTBOX)
       gi->selectbox.index = gi->selectbox.current_index;
 
-    DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
+    if (gi->type & GD_TYPE_TEXT_AREA)
+    {
+      insertCharIntoTextArea(gi, '\n');
+
+      DrawGadget(gi, DG_PRESSED, gi->direct_draw);
+    }
+    else
+    {
+      DrawGadget(gi, DG_UNPRESSED, gi->direct_draw);
 
-    gi->event.type = GD_EVENT_TEXT_RETURN;
+      gi->event.type = GD_EVENT_TEXT_RETURN;
+
+      last_gi = NULL;
+    }
 
     if (gi->event_mask & GD_EVENT_TEXT_RETURN)
       gi->callback_action(gi);
-
-    last_gi = NULL;
   }
-  else if (gi->type & GD_TYPE_TEXTINPUT)       /* only valid for text input */
+  else if (gi->type & GD_TYPE_TEXT_INPUT)      /* only valid for text input */
   {
     char text[MAX_GADGET_TEXTSIZE];
-    int text_length = strlen(gi->text.value);
-    int cursor_pos = gi->text.cursor_position;
+    int text_length = strlen(gi->textinput.value);
+    int cursor_pos = gi->textinput.cursor_position;
     char letter = getCharFromKey(key);
-    boolean legal_letter = (gi->type == GD_TYPE_TEXTINPUT_NUMERIC ?
+    boolean legal_letter = (gi->type == GD_TYPE_TEXT_INPUT_NUMERIC ?
                            letter >= '0' && letter <= '9' :
                            letter != 0);
 
-    if (legal_letter && text_length < gi->text.size)
+    if (legal_letter && text_length < gi->textinput.size)
     {
-      strcpy(text, gi->text.value);
-      strcpy(&gi->text.value[cursor_pos + 1], &text[cursor_pos]);
-      gi->text.value[cursor_pos] = letter;
-      gi->text.cursor_position++;
+      strcpy(text, gi->textinput.value);
+      strcpy(&gi->textinput.value[cursor_pos + 1], &text[cursor_pos]);
+      gi->textinput.value[cursor_pos] = letter;
+      gi->textinput.cursor_position++;
 
-      DrawGadget(gi, DG_PRESSED, DG_DIRECT);
+      DrawGadget(gi, DG_PRESSED, gi->direct_draw);
     }
     else if (key == KSYM_Left && cursor_pos > 0)
     {
-      gi->text.cursor_position--;
-      DrawGadget(gi, DG_PRESSED, DG_DIRECT);
+      gi->textinput.cursor_position--;
+
+      DrawGadget(gi, DG_PRESSED, gi->direct_draw);
     }
     else if (key == KSYM_Right && cursor_pos < text_length)
     {
-      gi->text.cursor_position++;
-      DrawGadget(gi, DG_PRESSED, DG_DIRECT);
+      gi->textinput.cursor_position++;
+
+      DrawGadget(gi, DG_PRESSED, gi->direct_draw);
     }
     else if (key == KSYM_BackSpace && cursor_pos > 0)
     {
-      strcpy(text, gi->text.value);
-      strcpy(&gi->text.value[cursor_pos - 1], &text[cursor_pos]);
-      gi->text.cursor_position--;
-      DrawGadget(gi, DG_PRESSED, DG_DIRECT);
+      strcpy(text, gi->textinput.value);
+      strcpy(&gi->textinput.value[cursor_pos - 1], &text[cursor_pos]);
+      gi->textinput.cursor_position--;
+
+      DrawGadget(gi, DG_PRESSED, gi->direct_draw);
     }
     else if (key == KSYM_Delete && cursor_pos < text_length)
     {
-      strcpy(text, gi->text.value);
-      strcpy(&gi->text.value[cursor_pos], &text[cursor_pos + 1]);
-      DrawGadget(gi, DG_PRESSED, DG_DIRECT);
+      strcpy(text, gi->textinput.value);
+      strcpy(&gi->textinput.value[cursor_pos], &text[cursor_pos + 1]);
+
+      DrawGadget(gi, DG_PRESSED, gi->direct_draw);
+    }
+  }
+  else if (gi->type & GD_TYPE_TEXT_AREA)       /* only valid for text area */
+  {
+    char text[MAX_GADGET_TEXTSIZE];
+    int text_length = strlen(gi->textarea.value);
+    int area_ysize = gi->textarea.ysize;
+    int cursor_x_pref = gi->textarea.cursor_x_preferred;
+    int cursor_y = gi->textarea.cursor_y;
+    int cursor_pos = gi->textarea.cursor_position;
+    char letter = getCharFromKey(key);
+    boolean legal_letter = (letter != 0);
+
+    if (legal_letter)
+    {
+      insertCharIntoTextArea(gi, letter);
+
+      DrawGadget(gi, DG_PRESSED, gi->direct_draw);
+    }
+    else if (key == KSYM_Left && cursor_pos > 0)
+    {
+      setTextAreaCursorPosition(gi, gi->textarea.cursor_position - 1);
+
+      DrawGadget(gi, DG_PRESSED, gi->direct_draw);
+    }
+    else if (key == KSYM_Right && cursor_pos < text_length)
+    {
+      setTextAreaCursorPosition(gi, gi->textarea.cursor_position + 1);
+
+      DrawGadget(gi, DG_PRESSED, gi->direct_draw);
+    }
+    else if (key == KSYM_Up && cursor_y > 0)
+    {
+      setTextAreaCursorXY(gi, cursor_x_pref, cursor_y - 1);
+      gi->textarea.cursor_x_preferred = cursor_x_pref;
+
+      DrawGadget(gi, DG_PRESSED, gi->direct_draw);
+    }
+    else if (key == KSYM_Down && cursor_y < area_ysize - 1)
+    {
+      setTextAreaCursorXY(gi, cursor_x_pref, cursor_y + 1);
+      gi->textarea.cursor_x_preferred = cursor_x_pref;
+
+      DrawGadget(gi, DG_PRESSED, gi->direct_draw);
+    }
+    else if (key == KSYM_BackSpace && cursor_pos > 0)
+    {
+      strcpy(text, gi->textarea.value);
+      strcpy(&gi->textarea.value[cursor_pos - 1], &text[cursor_pos]);
+
+      setTextAreaCursorPosition(gi, gi->textarea.cursor_position - 1);
+
+      DrawGadget(gi, DG_PRESSED, gi->direct_draw);
+    }
+    else if (key == KSYM_Delete && cursor_pos < text_length)
+    {
+      strcpy(text, gi->textarea.value);
+      strcpy(&gi->textarea.value[cursor_pos], &text[cursor_pos + 1]);
+
+      DrawGadget(gi, DG_PRESSED, gi->direct_draw);
     }
   }
   else if (gi->type & GD_TYPE_SELECTBOX)       /* only valid for selectbox */
@@ -1635,12 +1890,14 @@ void HandleGadgetsKeyInput(Key key)
     if (key == KSYM_Up && index > 0)
     {
       gi->selectbox.current_index--;
-      DrawGadget(gi, DG_PRESSED, DG_DIRECT);
+
+      DrawGadget(gi, DG_PRESSED, gi->direct_draw);
     }
     else if (key == KSYM_Down && index < num_values - 1)
     {
       gi->selectbox.current_index++;
-      DrawGadget(gi, DG_PRESSED, DG_DIRECT);
+
+      DrawGadget(gi, DG_PRESSED, gi->direct_draw);
     }
   }
 }
index 0bf8cc5a53ca322c81d5afb90d45a4e3ef71c7bd..160bd611a375311d5372208270f92499be2f21a3 100644 (file)
 #define GD_TYPE_CHECK_BUTTON           (1 << 2)
 #define GD_TYPE_RADIO_BUTTON           (1 << 3)
 #define GD_TYPE_DRAWING_AREA           (1 << 4)
-#define GD_TYPE_TEXTINPUT_ALPHANUMERIC (1 << 5)
-#define GD_TYPE_TEXTINPUT_NUMERIC      (1 << 6)
-#define GD_TYPE_SELECTBOX              (1 << 7)
-#define GD_TYPE_SCROLLBAR_VERTICAL     (1 << 8)
-#define GD_TYPE_SCROLLBAR_HORIZONTAL   (1 << 9)
+#define GD_TYPE_TEXT_INPUT_ALPHANUMERIC        (1 << 5)
+#define GD_TYPE_TEXT_INPUT_NUMERIC     (1 << 6)
+#define GD_TYPE_TEXT_AREA              (1 << 7)
+#define GD_TYPE_SELECTBOX              (1 << 8)
+#define GD_TYPE_SCROLLBAR_VERTICAL     (1 << 9)
+#define GD_TYPE_SCROLLBAR_HORIZONTAL   (1 << 10)
 
 #define GD_TYPE_BUTTON                 (GD_TYPE_NORMAL_BUTTON | \
                                         GD_TYPE_TEXT_BUTTON | \
@@ -37,8 +38,8 @@
                                         GD_TYPE_RADIO_BUTTON)
 #define GD_TYPE_SCROLLBAR              (GD_TYPE_SCROLLBAR_VERTICAL | \
                                         GD_TYPE_SCROLLBAR_HORIZONTAL)
-#define GD_TYPE_TEXTINPUT              (GD_TYPE_TEXTINPUT_ALPHANUMERIC | \
-                                        GD_TYPE_TEXTINPUT_NUMERIC)
+#define GD_TYPE_TEXT_INPUT             (GD_TYPE_TEXT_INPUT_ALPHANUMERIC | \
+                                        GD_TYPE_TEXT_INPUT_NUMERIC)
 
 /* gadget events */
 #define GD_EVENT_PRESSED               (1 << 0)
 #define GDI_SCROLLBAR_ITEM_POSITION    39
 #define GDI_INFO_TEXT                  40
 #define GDI_ACTIVE                     41
+#define GDI_DIRECT_DRAW                        42
 
 typedef void (*gadget_function)(void *);
 
@@ -157,6 +159,17 @@ struct GadgetTextInput
   int size;                            /* maximal size of input text */
 };
 
+struct GadgetTextArea
+{
+  char value[MAX_GADGET_TEXTSIZE];     /* text string in input field */
+  int cursor_position;                 /* actual text cursor position */
+  int cursor_x;                                /* actual x cursor position */
+  int cursor_y;                                /* actual y cursor position */
+  int cursor_x_preferred;              /* "preferred" x cursor position */
+  int size;                            /* maximal size of input text */
+  int xsize, ysize;                    /* size of text area (in chars) */
+};
+
 struct GadgetSelectbox
 {
   struct ValueTextInfo *options;       /* pointer to text/value array */
@@ -202,6 +215,7 @@ struct GadgetInfo
   int radio_nr;                                /* number of radio button series */
   boolean mapped;                      /* gadget is mapped on the screen */
   boolean active;                      /* gadget is active */
+  boolean direct_draw;                 /* directly draw to frontbuffer */
   int font;                            /* font to use when inactive */
   int font_active;                     /* font to use when active */
   struct GadgetBorder border;          /* gadget border design */
@@ -214,7 +228,8 @@ struct GadgetInfo
   gadget_function callback_action;     /* function for gadget action */
   struct GadgetDrawingArea drawing;    /* fields for drawing area gadget */
   struct GadgetTextButton textbutton;  /* fields for text button gadget */
-  struct GadgetTextInput text;         /* fields for text input gadget */
+  struct GadgetTextInput textinput;    /* fields for text input gadget */
+  struct GadgetTextArea textarea;      /* fields for text area gadget */
   struct GadgetSelectbox selectbox;    /* fields for selectbox gadget */
   struct GadgetScrollbar scrollbar;    /* fields for scrollbar gadget */
   struct GadgetInfo *next;             /* next list entry */
index 70b2a5d6b802ccbcbe16894a44e5778ced15cfaf..0d5a658e848b0bfef7f41385c9392a8a77ae4e3e 100644 (file)
@@ -1518,37 +1518,77 @@ boolean fileExists(char *filename)
   return (access(filename, F_OK) == 0);
 }
 
-boolean FileIsGraphic(char *filename)
+boolean fileHasPrefix(char *basename, char *prefix)
 {
-  if (strlen(filename) > 4 &&
-      strcmp(&filename[strlen(filename) - 4], ".pcx") == 0)
+  static char *basename_lower = NULL;
+  int basename_length, prefix_length;
+
+  if (basename_lower != NULL)
+    free(basename_lower);
+
+  if (basename == NULL || prefix == NULL)
+    return FALSE;
+
+  basename_lower = getStringToLower(basename);
+  basename_length = strlen(basename_lower);
+  prefix_length = strlen(prefix);
+
+  if (basename_length > prefix_length + 1 &&
+      basename_lower[prefix_length] == '.' &&
+      strncmp(basename_lower, prefix, prefix_length) == 0)
     return TRUE;
 
   return FALSE;
 }
 
-boolean FileIsSound(char *basename)
+boolean fileHasSuffix(char *basename, char *suffix)
 {
-  if (strlen(basename) > 4 &&
-      strcmp(&basename[strlen(basename) - 4], ".wav") == 0)
+  static char *basename_lower = NULL;
+  int basename_length, suffix_length;
+
+  if (basename_lower != NULL)
+    free(basename_lower);
+
+  if (basename == NULL || suffix == NULL)
+    return FALSE;
+
+  basename_lower = getStringToLower(basename);
+  basename_length = strlen(basename_lower);
+  suffix_length = strlen(suffix);
+
+  if (basename_length > suffix_length + 1 &&
+      basename_lower[basename_length - suffix_length - 1] == '.' &&
+      strcmp(&basename_lower[basename_length - suffix_length], suffix) == 0)
     return TRUE;
 
   return FALSE;
 }
 
-boolean FileIsMusic(char *basename)
+boolean FileIsGraphic(char *basename)
 {
-  /* "music" can be a WAV (loop) file or (if compiled with SDL) a MOD file */
+  return fileHasSuffix(basename, "pcx");
+}
 
+boolean FileIsSound(char *basename)
+{
+  return fileHasSuffix(basename, "wav");
+}
+
+boolean FileIsMusic(char *basename)
+{
   if (FileIsSound(basename))
     return TRUE;
 
 #if defined(TARGET_SDL)
-  if (strlen(basename) > 4 &&
-      (strcmp(&basename[strlen(basename) - 4], ".mod") == 0 ||
-       strcmp(&basename[strlen(basename) - 4], ".MOD") == 0 ||
-       strncmp(basename, "mod.", 4) == 0 ||
-       strncmp(basename, "MOD.", 4) == 0))
+  if (fileHasPrefix(basename, "mod") ||
+      fileHasSuffix(basename, "mod") ||
+      fileHasSuffix(basename, "s3m") ||
+      fileHasSuffix(basename, "it") ||
+      fileHasSuffix(basename, "xm") ||
+      fileHasSuffix(basename, "midi") ||
+      fileHasSuffix(basename, "mid") ||
+      fileHasSuffix(basename, "mp3") ||
+      fileHasSuffix(basename, "ogg"))
     return TRUE;
 #endif
 
index 95ef6eef6e9d97dcf3f724c45a1febc7af3d2022..74123228f2b2185f43100f4c3b2d5c19b70f356a 100644 (file)
@@ -1277,19 +1277,26 @@ static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
 {
   static struct MouseCursorInfo *last_cursor_info = NULL;
+  static struct MouseCursorInfo *last_cursor_info2 = NULL;
   static SDL_Cursor *cursor_default = NULL;
   static SDL_Cursor *cursor_current = NULL;
 
+  /* if invoked for the first time, store the SDL default cursor */
   if (cursor_default == NULL)
     cursor_default = SDL_GetCursor();
 
+  /* only create new cursor if cursor info (custom only) has changed */
   if (cursor_info != NULL && cursor_info != last_cursor_info)
   {
     cursor_current = create_cursor(cursor_info);
     last_cursor_info = cursor_info;
   }
 
-  SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
+  /* only set new cursor if cursor info (custom or NULL) has changed */
+  if (cursor_info != last_cursor_info2)
+    SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
+
+  last_cursor_info2 = cursor_info;
 }
 
 
index 2a5eb1ab12cdb0263a589a056082606f0e816c5a..4d8abb26e8594b0b83c6a89171d5227d1d40c900 100644 (file)
@@ -859,8 +859,13 @@ void dumpTreeInfo(TreeInfo *node, int depth)
     for (i=0; i<(depth + 1) * 3; i++)
       printf(" ");
 
+#if 1
+    printf("filename == '%s' ['%s', '%s'] [%d])\n",
+          node->filename, node->fullpath, node->basepath, node->user_defined);
+#else
     printf("filename == '%s' (%s) [%s] (%d)\n",
           node->filename, node->name, node->identifier, node->sort_priority);
+#endif
 
     if (node->node_group != NULL)
       dumpTreeInfo(node->node_group, depth + 1);
@@ -1619,6 +1624,45 @@ static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
 #endif
 }
 
+static void freeTreeInfo(TreeInfo *ldi)
+{
+  if (ldi->filename)
+    free(ldi->filename);
+  if (ldi->fullpath)
+    free(ldi->fullpath);
+  if (ldi->basepath)
+    free(ldi->basepath);
+  if (ldi->identifier)
+    free(ldi->identifier);
+
+  if (ldi->name)
+    free(ldi->name);
+  if (ldi->name_sorting)
+    free(ldi->name_sorting);
+  if (ldi->author)
+    free(ldi->author);
+
+  if (ldi->class_desc)
+    free(ldi->class_desc);
+
+  if (ldi->type == TREE_TYPE_LEVEL_DIR)
+  {
+    if (ldi->graphics_set)
+      free(ldi->graphics_set);
+    if (ldi->sounds_set)
+      free(ldi->sounds_set);
+    if (ldi->music_set)
+      free(ldi->music_set);
+
+    if (ldi->graphics_path)
+      free(ldi->graphics_path);
+    if (ldi->sounds_path)
+      free(ldi->sounds_path);
+    if (ldi->music_path)
+      free(ldi->music_path);
+  }
+}
+
 void setSetupInfo(struct TokenInfo *token_info,
                  int token_nr, char *token_value)
 {
@@ -1710,6 +1754,18 @@ static void createParentTreeInfoNode(TreeInfo *node_parent)
   ti_new->node_parent = node_parent;
   ti_new->parent_link = TRUE;
 
+#if 1
+  setString(&ti_new->identifier, node_parent->identifier);
+  setString(&ti_new->name, ".. (parent directory)");
+  setString(&ti_new->name_sorting, ti_new->name);
+
+  setString(&ti_new->filename, "..");
+  setString(&ti_new->fullpath, node_parent->fullpath);
+
+  ti_new->sort_priority = node_parent->sort_priority;
+
+  setString(&ti_new->class_desc, getLevelClassDescription(ti_new));
+#else
   ti_new->identifier = getStringCopy(node_parent->identifier);
   ti_new->name = ".. (parent directory)";
   ti_new->name_sorting = getStringCopy(ti_new->name);
@@ -1718,7 +1774,9 @@ static void createParentTreeInfoNode(TreeInfo *node_parent)
   ti_new->fullpath = getStringCopy(node_parent->fullpath);
 
   ti_new->sort_priority = node_parent->sort_priority;
+
   ti_new->class_desc = getLevelClassDescription(ti_new);
+#endif
 
   pushTreeInfo(&node_parent->node_group, ti_new);
 }
@@ -1765,11 +1823,16 @@ static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
                 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
   *leveldir_new = ldi;
 
+#if 1
+  if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
+    setString(&leveldir_new->name, leveldir_new->filename);
+#else
   if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
   {
     free(leveldir_new->name);
     leveldir_new->name = getStringCopy(leveldir_new->filename);
   }
+#endif
 
   DrawInitText(leveldir_new->name, 150, FC_YELLOW);
 
@@ -1781,12 +1844,12 @@ static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
 
   if (node_parent == NULL)             /* top level group */
   {
-    leveldir_new->basepath = level_directory;
-    leveldir_new->fullpath = leveldir_new->filename;
+    leveldir_new->basepath = getStringCopy(level_directory);
+    leveldir_new->fullpath = getStringCopy(leveldir_new->filename);
   }
   else                                 /* sub level group */
   {
-    leveldir_new->basepath = node_parent->basepath;
+    leveldir_new->basepath = getStringCopy(node_parent->basepath);
     leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
   }
 
@@ -1796,11 +1859,20 @@ static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
   leveldir_new->last_level =
     leveldir_new->first_level + leveldir_new->levels - 1;
 
+#if 1
+  leveldir_new->user_defined =
+    (strcmp(leveldir_new->basepath, options.level_directory) != 0);
+#else
   leveldir_new->user_defined =
     (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
+#endif
 
   leveldir_new->color = LEVELCOLOR(leveldir_new);
+#if 1
+  setString(&leveldir_new->class_desc, getLevelClassDescription(leveldir_new));
+#else
   leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
+#endif
 
   leveldir_new->handicap_level =       /* set handicap to default value */
     (leveldir_new->user_defined ?
@@ -1981,11 +2053,16 @@ static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
                   getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
     *artwork_new = ldi;
 
+#if 1
+    if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
+      setString(&artwork_new->name, artwork_new->filename);
+#else
     if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
     {
       free(artwork_new->name);
       artwork_new->name = getStringCopy(artwork_new->filename);
     }
+#endif
 
 #if 0
     DrawInitText(artwork_new->name, 150, FC_YELLOW);
@@ -2009,42 +2086,86 @@ static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
     artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
   }
 
+#if 1
+  artwork_new->user_defined =
+    (strcmp(artwork_new->basepath, OPTIONS_ARTWORK_DIRECTORY(type)) != 0);
+#else
   artwork_new->user_defined =
     (artwork_new->basepath == OPTIONS_ARTWORK_DIRECTORY(type) ? FALSE : TRUE);
+#endif
 
   /* (may use ".sort_priority" from "setup_file_hash" above) */
   artwork_new->color = ARTWORKCOLOR(artwork_new);
+#if 1
+  setString(&artwork_new->class_desc, getLevelClassDescription(artwork_new));
+#else
   artwork_new->class_desc = getLevelClassDescription(artwork_new);
+#endif
 
   if (setup_file_hash == NULL) /* (after determining ".user_defined") */
   {
+#if 0
     if (artwork_new->name != NULL)
+    {
       free(artwork_new->name);
+      artwork_new->name = NULL;
+    }
+#endif
+
+#if 0
+    if (artwork_new->identifier != NULL)
+    {
+      free(artwork_new->identifier);
+      artwork_new->identifier = NULL;
+    }
+#endif
 
     if (strcmp(artwork_new->filename, ".") == 0)
     {
       if (artwork_new->user_defined)
       {
+#if 1
+       setString(&artwork_new->identifier, "private");
+#else
        artwork_new->identifier = getStringCopy("private");
+#endif
        artwork_new->sort_priority = ARTWORKCLASS_USER;
       }
       else
       {
+#if 1
+       setString(&artwork_new->identifier, "classic");
+#else
        artwork_new->identifier = getStringCopy("classic");
+#endif
        artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
       }
 
       /* set to new values after changing ".sort_priority" */
       artwork_new->color = ARTWORKCOLOR(artwork_new);
+#if 1
+      setString(&artwork_new->class_desc,
+               getLevelClassDescription(artwork_new));
+#else
       artwork_new->class_desc = getLevelClassDescription(artwork_new);
+#endif
     }
     else
     {
+#if 1
+      setString(&artwork_new->identifier, artwork_new->filename);
+#else
       artwork_new->identifier = getStringCopy(artwork_new->filename);
+#endif
     }
 
+#if 1
+    setString(&artwork_new->name, artwork_new->identifier);
+    setString(&artwork_new->name_sorting, artwork_new->name);
+#else
     artwork_new->name = getStringCopy(artwork_new->identifier);
     artwork_new->name_sorting = getStringCopy(artwork_new->name);
+#endif
   }
 
   DrawInitText(artwork_new->name, 150, FC_YELLOW);
@@ -2122,6 +2243,15 @@ static TreeInfo *getDummyArtworkInfo(int type)
 
   setTreeInfoToDefaults(artwork_new, type);
 
+#if 1
+  setString(&artwork_new->filename, UNDEFINED_FILENAME);
+  setString(&artwork_new->fullpath, UNDEFINED_FILENAME);
+  setString(&artwork_new->basepath, UNDEFINED_FILENAME);
+
+  setString(&artwork_new->identifier,   UNDEFINED_FILENAME);
+  setString(&artwork_new->name,         UNDEFINED_FILENAME);
+  setString(&artwork_new->name_sorting, UNDEFINED_FILENAME);
+#else
   artwork_new->filename = getStringCopy(UNDEFINED_FILENAME);
   artwork_new->fullpath = getStringCopy(UNDEFINED_FILENAME);
   artwork_new->basepath = getStringCopy(UNDEFINED_FILENAME);
@@ -2132,6 +2262,7 @@ static TreeInfo *getDummyArtworkInfo(int type)
   artwork_new->identifier   = getStringCopy(UNDEFINED_FILENAME);
   artwork_new->name         = getStringCopy(UNDEFINED_FILENAME);
   artwork_new->name_sorting = getStringCopy(UNDEFINED_FILENAME);
+#endif
 
   return artwork_new;
 }
@@ -2316,6 +2447,7 @@ void LoadLevelArtworkInfo()
 
 static void SaveUserLevelInfo()
 {
+  LevelDirTree *level_info;
   char *filename;
   FILE *file;
   int i;
@@ -2329,20 +2461,21 @@ static void SaveUserLevelInfo()
     return;
   }
 
+  level_info = newTreeInfo();
+
   /* always start with reliable default values */
-  setTreeInfoToDefaults(&ldi, TREE_TYPE_LEVEL_DIR);
+  setTreeInfoToDefaults(level_info, TREE_TYPE_LEVEL_DIR);
 
-#if 0
-  /* !!! FIX ME !!! */
-  setString(&ldi.name, getLoginName());
-  setString(&ldi.author, getRealName());
-  ldi.levels = 100;
-  ldi.first_level = 1;
-  ldi.sort_priority = LEVELCLASS_USER_START;
-  ldi.readonly = FALSE;
-  setString(&ldi.graphics_set, GFX_CLASSIC_SUBDIR);
-  setString(&ldi.sounds_set,   SND_CLASSIC_SUBDIR);
-  setString(&ldi.music_set,    MUS_CLASSIC_SUBDIR);
+#if 1
+  setString(&level_info->name, getLoginName());
+  setString(&level_info->author, getRealName());
+  level_info->levels = 100;
+  level_info->first_level = 1;
+  level_info->sort_priority = LEVELCLASS_USER_START;
+  level_info->readonly = FALSE;
+  setString(&level_info->graphics_set, GFX_CLASSIC_SUBDIR);
+  setString(&level_info->sounds_set,   SND_CLASSIC_SUBDIR);
+  setString(&level_info->music_set,    MUS_CLASSIC_SUBDIR);
 #else
   ldi.name = getStringCopy(getLoginName());
   ldi.author = getStringCopy(getRealName());
@@ -2358,6 +2491,7 @@ static void SaveUserLevelInfo()
   fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
                                                 getCookie("LEVELINFO")));
 
+  ldi = *level_info;
   for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
     if (i != LEVELINFO_TOKEN_IDENTIFIER &&
        i != LEVELINFO_TOKEN_NAME_SORTING &&
@@ -2365,9 +2499,11 @@ static void SaveUserLevelInfo()
       fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
 
   fclose(file);
-  free(filename);
 
   SetFilePermissions(filename, PERMS_PRIVATE);
+
+  freeTreeInfo(level_info);
+  free(filename);
 }
 
 char *getSetupValue(int type, void *value)
@@ -2514,9 +2650,10 @@ void SaveLevelSetup_LastSeries()
                                               level_subdir));
 
   fclose(file);
-  free(filename);
 
   SetFilePermissions(filename, PERMS_PRIVATE);
+
+  free(filename);
 }
 
 static void checkSeriesInfo()
@@ -2663,7 +2800,8 @@ void SaveLevelSetup_SeriesInfo()
                                               handicap_level_str));
 
   fclose(file);
-  free(filename);
 
   SetFilePermissions(filename, PERMS_PRIVATE);
+
+  free(filename);
 }
index 0299521267dd802ccd5024467a15590fb286ed22..72681647ed847bbfeba8edf3ea3172408532e22a 100644 (file)
@@ -314,7 +314,7 @@ static void InitAudioDevice_Linux(struct AudioFormatInfo *afmt)
   /* "ioctl()" expects pointer to 'int' value for stereo flag
      (boolean is defined as 'char', which will not work here) */
   unsigned int fragment_spec = 0;
-  int fragment_size_query;
+  int fragment_size_query = -1;
   int stereo = TRUE;
   struct
   {
@@ -507,6 +507,8 @@ static void WriteReloadInfoToPipe(char *set_identifier, int type)
   if (leveldir_current == NULL)                /* should never happen */
     Error(ERR_EXIT, "leveldir_current == NULL");
 
+  memset(&snd_ctrl, 0, sizeof(SoundControl));  /* to make valgrind happy */
+
   snd_ctrl.active = FALSE;
   snd_ctrl.state = type;
   snd_ctrl.data_len = strlen(set_identifier) + 1;
@@ -2038,6 +2040,8 @@ void PlaySoundExt(int nr, int volume, int stereo_position, int state)
   else if (stereo_position > SOUND_MAX_RIGHT)
     stereo_position = SOUND_MAX_RIGHT;
 
+  memset(&snd_ctrl, 0, sizeof(SoundControl));  /* to make valgrind happy */
+
   snd_ctrl.active = TRUE;
   snd_ctrl.nr = nr;
   snd_ctrl.volume = volume;
@@ -2092,6 +2096,8 @@ void StopSoundExt(int nr, int state)
   if (!audio.sound_available)
     return;
 
+  memset(&snd_ctrl, 0, sizeof(SoundControl));  /* to make valgrind happy */
+
   snd_ctrl.active = FALSE;
   snd_ctrl.nr = nr;
   snd_ctrl.state = state;
index a9f1efa1786ad01efeb85cb7b8d2321da3640dee..73ec0f79aae512a66301a2563628baf24bfb7352 100644 (file)
@@ -485,6 +485,8 @@ struct SetupEditorInfo
   boolean el_chars;
   boolean el_custom;
   boolean el_custom_more;
+
+  boolean el_headlines;
 };
 
 struct SetupShortcutInfo
index a4b60302110914df7d3b35640470c9b9ddbc95e3..c5406657e54873bee17b59129a6557f5b3a6f80d 100644 (file)
@@ -259,6 +259,9 @@ void DrawTextExt(DrawBuffer *dst_bitmap, int dst_x, int dst_y, char *text,
   {
     char c = *text_ptr++;
 
+    if (c == '\n')
+      c = ' ';         /* print space instaed of newline */
+
     getFontCharSource(font_nr, c, &src_bitmap, &src_x, &src_y);
 
     if (mask_mode == BLIT_INVERSE)     /* special mode for text gadgets */
@@ -322,3 +325,31 @@ void DrawTextExt(DrawBuffer *dst_bitmap, int dst_x, int dst_y, char *text,
     dst_x += font_width;
   }
 }
+
+void DrawTextToTextArea(int x, int y, char *text, int font_nr,
+                       int area_xsize, int area_ysize, int mask_mode)
+{
+  int area_line = 0;
+  int font_height = getFontHeight(font_nr);
+
+  if (text == NULL)
+    return;
+
+  while (*text && area_line < area_ysize)
+  {
+    char buffer[MAX_OUTPUT_LINESIZE + 1];
+    int i;
+
+    for (i=0; i < area_xsize && *text; i++)
+      if ((buffer[i] = *text++) == '\n')
+       break;
+    buffer[i] = '\0';
+
+    DrawTextExt(drawto, x, y + area_line * font_height, buffer, font_nr,
+               mask_mode);
+
+    area_line++;
+  }
+
+  redraw_mask |= REDRAW_FIELD;
+}
index fd1e8c57d558a1efa501c4b3470183e666930fde..bde8ba9d661058af6a874f67a4bb1d8ebc558019 100644 (file)
@@ -66,5 +66,6 @@ void DrawTextF(int, int, int, char *, ...);
 void DrawTextFCentered(int, int, char *, ...);
 void DrawText(int, int, char *, int);
 void DrawTextExt(DrawBuffer *, int, int, char *, int, int);
+void DrawTextToTextArea(int, int, char *, int, int, int, int);
 
 #endif /* TEXT_H */
index 01229f019b0b7285fe295fdc135bce184504246a..048696f6cd365fb4322959f5d7da4fcced91bfed 100644 (file)
@@ -81,17 +81,18 @@ int getAnimationFrame(int num_frames, int delay, int mode, int start_frame,
 /* toon animation functions                                                  */
 /* ========================================================================= */
 
-static int get_toon_direction(char *direction_raw)
+static int get_toon_direction(char *direction_string_raw)
 {
-  static char *direction = NULL;
+  char *direction_string = getStringToLower(direction_string_raw);
+  int direction = (strcmp(direction_string, "left")  == 0 ? MV_LEFT :
+                  strcmp(direction_string, "right") == 0 ? MV_RIGHT :
+                  strcmp(direction_string, "up")    == 0 ? MV_UP :
+                  strcmp(direction_string, "down")  == 0 ? MV_DOWN :
+                  MV_NO_MOVING);
 
-  /* !!! MEMORY LEAK HERE! FIX IT! !!! */
-  setString(&direction, getStringToLower(direction_raw));
+  free(direction_string);
 
-  return (strcmp(direction, "left")  == 0 ? MV_LEFT :
-         strcmp(direction, "right") == 0 ? MV_RIGHT :
-         strcmp(direction, "up")    == 0 ? MV_UP :
-         strcmp(direction, "down")  == 0 ? MV_DOWN : MV_NO_MOVING);
+  return direction;
 }
 
 void InitToonScreen(Bitmap *save_buffer,
@@ -310,6 +311,7 @@ void HandleAnimation(int mode)
 {
   static unsigned long animstart_delay = -1;
   static unsigned long animstart_delay_value = 0;
+  static boolean anim_running = FALSE;
   static boolean anim_restart = TRUE;
   static boolean reset_delay = TRUE;
   static int toon_nr = 0;
@@ -326,12 +328,17 @@ void HandleAnimation(int mode)
   {
     case ANIM_START:
       screen_info.prepare_backbuffer_function();
+
+      anim_running = TRUE;
       anim_restart = TRUE;
       reset_delay = TRUE;
 
       return;
 
     case ANIM_CONTINUE:
+      if (!anim_running)
+       return;
+
       break;
 
     case ANIM_STOP:
@@ -343,6 +350,8 @@ void HandleAnimation(int mode)
       screen_info.update_function();
       setup.direct_draw = draw_mode;
 
+      anim_running = FALSE;
+
       return;
 
     default:
index 479e0122634d42a0a02e6964d4c0c175f6469794..1d85ad187af7805c82e8926d3f0cb206badce0a8 100644 (file)
@@ -50,7 +50,7 @@ short                 StorePlayer[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
 short                  Back[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
 boolean                        Stop[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
 boolean                        Pushed[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
-boolean                        Changing[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
+boolean                        Changed[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
 short                  JustStopped[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
 short                  AmoebaNr[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
 short                  AmoebaCnt[MAX_NUM_AMOEBA];
@@ -3687,6 +3687,7 @@ struct SpecialSuffixInfo special_suffix_info[NUM_SPECIAL_GFX_ARGS + 1] =
   { ".EDITOR",         GAME_MODE_EDITOR,               },
   { ".INFO",           GAME_MODE_INFO,                 },
   { ".SETUP",          GAME_MODE_SETUP,                },
+  { ".PLAYING",                GAME_MODE_PLAYING,              },
   { ".DOOR",           GAME_MODE_PSEUDO_DOOR,          },
   { ".PREVIEW",                GAME_MODE_PSEUDO_PREVIEW,       },
   { ".CRUMBLED",       GAME_MODE_PSEUDO_CRUMBLED,      },
index 5a9ef38886b5bbe8996832e4c990616b5213d3f5..76be4a459f3c0445c4b6fddd3f36ad1b7ca64a78 100644 (file)
 #define CE_OTHER_GETS_PUSHED   13
 #define CE_OTHER_GETS_COLLECTED        14
 #define CE_OTHER_GETS_DROPPED  15
+#define CE_BY_PLAYER           16 /* obsolete; map'd to CE_BY_DIRECT_ACTION */
+#define CE_BY_COLLISION                17 /* obsolete; map'd to CE_BY_DIRECT_ACTION */
+#define CE_BY_OTHER_ACTION     18      /* activates other element events */
+#define CE_BY_DIRECT_ACTION    19      /* activates direct element events */
+#define CE_OTHER_GETS_DIGGED   20
 
-/* values for activating change events (also stored in level file!) */
-#define CE_BY_PLAYER           16
-#define CE_BY_COLLISION                17
-#define CE_BY_OTHER            18
-
-#define NUM_CHANGE_EVENTS      19
+#define NUM_CHANGE_EVENTS      21
 
 #define CE_BITMASK_DEFAULT     0
 
 #define CH_EVENT_BIT(c)                (1 << (c))
-#define CH_EVENT_VAR(e)                (element_info[e].change.events)
+#define CH_EVENT_VAR(e)                (element_info[e].change->events)
+#define CH_ANY_EVENT_VAR(e)    (element_info[e].change_events)
 
 #define HAS_CHANGE_EVENT(e,c)  (IS_CUSTOM_ELEMENT(e) &&                  \
                                 (CH_EVENT_VAR(e) & CH_EVENT_BIT(c)) != 0)
+#define HAS_ANY_CHANGE_EVENT(e,c) (IS_CUSTOM_ELEMENT(e) &&               \
+                                (CH_ANY_EVENT_VAR(e) & CH_EVENT_BIT(c)) != 0)
 #define SET_CHANGE_EVENT(e,c,v)        (IS_CUSTOM_ELEMENT(e) ?                   \
                                 ((v) ?                                   \
                                  (CH_EVENT_VAR(e) |=  CH_EVENT_BIT(c)) : \
 #define IS_LOOP_SOUND(s)       (sound_info[s].loop)
 
 
+/* fundamental game speed values */
+#define GAME_FRAME_DELAY       20      /* frame delay in milliseconds */
+#define FFWD_FRAME_DELAY       10      /* 200% speed for fast forward */
+#define FRAMES_PER_SECOND      (1000 / GAME_FRAME_DELAY)
+#define MICROLEVEL_SCROLL_DELAY        50      /* delay for scrolling micro level */
+#define MICROLEVEL_LABEL_DELAY 250     /* delay for micro level label */
+
 /* boundaries of arrays etc. */
 #define MAX_LEVEL_NAME_LEN     32
 #define MAX_LEVEL_AUTHOR_LEN   32
 #define MAX_ELEMENT_NAME_LEN   32
-#define MAX_TAPELEN            (1000 * 50)     /* max. time * framerate */
+#define MAX_TAPELEN            (1000 * FRAMES_PER_SECOND) /* max.time x fps */
 #define MAX_SCORE_ENTRIES      100
 #define MAX_NUM_AMOEBA         100
 #define MAX_INVENTORY_SIZE     1000
+#define MIN_ENVELOPE_XSIZE     1
+#define MIN_ENVELOPE_YSIZE     1
+#define MAX_ENVELOPE_XSIZE     30
+#define MAX_ENVELOPE_YSIZE     20
+#define MAX_ENVELOPE_TEXT_LEN  (MAX_ENVELOPE_XSIZE * MAX_ENVELOPE_YSIZE)
+#define MIN_CHANGE_PAGES       1
+#define MAX_CHANGE_PAGES       10
 
 /* values for elements with content */
 #define MIN_ELEMENT_CONTENTS   1
 
 #define LEVEL_SCORE_ELEMENTS   16      /* level elements with score */
 
-/* fundamental game speed values */
-#define GAME_FRAME_DELAY       20      /* frame delay in milliseconds */
-#define FFWD_FRAME_DELAY       10      /* 200% speed for fast forward */
-#define FRAMES_PER_SECOND      (1000 / GAME_FRAME_DELAY)
-#define MICROLEVEL_SCROLL_DELAY        50      /* delay for scrolling micro level */
-#define MICROLEVEL_LABEL_DELAY 250     /* delay for micro level label */
-
 /* often used screen positions */
 #define SX                     8
 #define SY                     8
 #define GFX_SPECIAL_ARG_EDITOR                 3
 #define GFX_SPECIAL_ARG_INFO                   4
 #define GFX_SPECIAL_ARG_SETUP                  5
-#define GFX_SPECIAL_ARG_DOOR                   6
-#define GFX_SPECIAL_ARG_PREVIEW                        7
-#define GFX_SPECIAL_ARG_CRUMBLED               8
+#define GFX_SPECIAL_ARG_PLAYING                        6
+#define GFX_SPECIAL_ARG_DOOR                   7
+#define GFX_SPECIAL_ARG_PREVIEW                        8
+#define GFX_SPECIAL_ARG_CRUMBLED               9
 
-#define NUM_SPECIAL_GFX_ARGS                   9
+#define NUM_SPECIAL_GFX_ARGS                   10
 
 
 /* values for image configuration suffixes */
 #define GFX_ARG_GLOBAL_SYNC                    15
 #define GFX_ARG_CRUMBLED_LIKE                  16
 #define GFX_ARG_DIGGABLE_LIKE                  17
-#define GFX_ARG_STEP_OFFSET                    18
-#define GFX_ARG_STEP_DELAY                     19
-#define GFX_ARG_DIRECTION                      20
-#define GFX_ARG_POSITION                       21
-#define GFX_ARG_DRAW_XOFFSET                   22
-#define GFX_ARG_DRAW_YOFFSET                   23
-#define GFX_ARG_NAME                           24
+#define GFX_ARG_BORDER_SIZE                    18
+#define GFX_ARG_STEP_OFFSET                    19
+#define GFX_ARG_STEP_DELAY                     20
+#define GFX_ARG_DIRECTION                      21
+#define GFX_ARG_POSITION                       22
+#define GFX_ARG_DRAW_XOFFSET                   23
+#define GFX_ARG_DRAW_YOFFSET                   24
+#define GFX_ARG_DRAW_MASKED                    25
+#define GFX_ARG_NAME                           26
 
-#define NUM_GFX_ARGS                           25
+#define NUM_GFX_ARGS                           27
 
 
 /* values for sound configuration suffixes */
 #define GAME_MODE_EDITOR                       3
 #define GAME_MODE_INFO                         4
 #define GAME_MODE_SETUP                                5
-#define GAME_MODE_PSEUDO_DOOR                  6
-#define GAME_MODE_PSEUDO_PREVIEW               7
-#define GAME_MODE_PSEUDO_CRUMBLED              8
+#define GAME_MODE_PLAYING                      6
+#define GAME_MODE_PSEUDO_DOOR                  7
+#define GAME_MODE_PSEUDO_PREVIEW               8
+#define GAME_MODE_PSEUDO_CRUMBLED              9
 
 /* there are no special config file suffixes for these modes */
-#define GAME_MODE_PLAYING                      9
 #define GAME_MODE_PSEUDO_TYPENAME              10
 #define GAME_MODE_QUIT                         11
 
 #define PROGRAM_VERSION_MAJOR  3
 #define PROGRAM_VERSION_MINOR  0
-#define PROGRAM_VERSION_PATCH  2
+#define PROGRAM_VERSION_PATCH  3
 #define PROGRAM_VERSION_RELEASE        0
-#define PROGRAM_VERSION_STRING "3.0.2"
+#define PROGRAM_VERSION_STRING "3.0.3"
 
 #define PROGRAM_TITLE_STRING   "Rocks'n'Diamonds"
 #define PROGRAM_AUTHOR_STRING  "Holger Schemel"
@@ -1112,12 +1125,13 @@ struct LevelInfo
   boolean encoding_16bit_yamyam;       /* yamyam contains 16-bit elements */
   boolean encoding_16bit_amoeba;       /* amoeba contains 16-bit elements */
 
-  int fieldx;
-  int fieldy;
+  int fieldx, fieldy;
   int time;
   int gems_needed;
   char name[MAX_LEVEL_NAME_LEN + 1];
   char author[MAX_LEVEL_AUTHOR_LEN + 1];
+  char envelope[MAX_ENVELOPE_TEXT_LEN + 1];
+  int envelope_xsize, envelope_ysize;
   int score[LEVEL_SCORE_ELEMENTS];
   int yamyam_content[MAX_ELEMENT_CONTENTS][3][3];
   int num_yamyam_contents;
@@ -1135,7 +1149,7 @@ struct LevelInfo
 
   boolean use_custom_template; /* use custom properties from template file */
 
-  boolean no_level_file;
+  boolean no_level_file;       /* set for currently undefined levels */
 };
 
 struct TapeInfo
@@ -1225,6 +1239,8 @@ struct DoorInfo
 
 struct ElementChangeInfo
 {
+  boolean can_change;          /* use or ignore this change info */
+
   unsigned long events;                /* bitfield for change events */
 
   short target_element;                /* target element after change */
@@ -1249,6 +1265,11 @@ struct ElementChangeInfo
   void (*pre_change_function)(int x, int y);
   void (*change_function)(int x, int y);
   void (*post_change_function)(int x, int y);
+
+  /* ---------- internal values used in level editor ---------- */
+
+  int direct_action;           /* change triggered by actions on element */
+  int other_action;            /* change triggered by other element actions */
 };
 
 struct ElementInfo
@@ -1297,7 +1318,18 @@ struct ElementInfo
 
   int content[3][3];           /* new elements after explosion */
 
-  struct ElementChangeInfo change;
+  struct ElementChangeInfo *change_page; /* actual list of change pages */
+  struct ElementChangeInfo *change;     /* pointer to current change page */
+
+  int num_change_pages;                /* actual number of change pages */
+  int current_change_page;     /* currently edited change page */
+
+  /* ---------- internal values used at runtime when playing ---------- */
+
+  unsigned long change_events; /* bitfield for combined change events */
+
+  int event_page_num[NUM_CHANGE_EVENTS]; /* page number for each event */
+  struct ElementChangeInfo *event_page[NUM_CHANGE_EVENTS]; /* page for event */
 
   /* ---------- internal values used in level editor ---------- */
 
@@ -1307,9 +1339,6 @@ struct ElementInfo
   int smash_targets;           /* can smash player/enemies/everything */
   int deadliness;              /* deadly when running/colliding/touching */
   int consistency;             /* indestructible/can explode */
-  int change_player_action;    /* touched/pressed/pushed by player */
-  int change_collide_action;   /* collision/impact/smashed */
-  int change_other_action;     /* various change actions */
 
   boolean can_explode_by_fire; /* element explodes by fire */
   boolean can_explode_smashed; /* element explodes when smashed */
@@ -1343,12 +1372,15 @@ struct GraphicInfo
   boolean anim_global_sync;
   int crumbled_like;           /* element for cloning crumble graphics */
   int diggable_like;           /* element for cloning digging graphics */
+  int border_size;             /* border size for "crumbled" graphics */
 
   int step_offset;             /* optional step offset of toon animations */
   int step_delay;              /* optional step delay of toon animations */
 
   int draw_x, draw_y;          /* optional offset for drawing fonts chars */
 
+  int draw_masked;             /* optional setting for drawing envelope gfx */
+
 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
   Pixmap clip_mask;            /* single-graphic-only clip mask for X11 */
   GC clip_gc;                  /* single-graphic-only clip gc for X11 */
@@ -1409,7 +1441,7 @@ extern short                      StorePlayer[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
 extern short                   Back[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
 extern boolean                 Stop[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
 extern boolean                 Pushed[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
-extern boolean                 Changing[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
+extern boolean                 Changed[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
 extern short                   JustStopped[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
 extern short                   AmoebaNr[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
 extern short                   AmoebaCnt[MAX_NUM_AMOEBA];
index b158d0dbce13ec9d4bf5b2462222468f811b8db0..0965eb9efcfe60aec3c14658bb3b7e28df351cfa 100644 (file)
@@ -331,7 +331,7 @@ static void gotoTopLevelDir()
 
 void HandleMainMenu(int mx, int my, int dx, int dy, int button)
 {
-  static int choice = 0;
+  static int choice = 5;
   int x = 0;
   int y = choice;
 
@@ -472,8 +472,6 @@ void HandleMainMenu(int mx, int my, int dx, int dy, int button)
     }
   }
 
-  BackToFront();
-
   out:
 
   if (game_status == GAME_MODE_MAIN)
@@ -481,6 +479,8 @@ void HandleMainMenu(int mx, int my, int dx, int dy, int button)
     DrawMicroLevel(MICROLEV_XPOS, MICROLEV_YPOS, FALSE);
     DoAnimation();
   }
+
+  BackToFront();
 }
 
 
@@ -1131,10 +1131,9 @@ void HandleHelpScreen(int button)
 #else
     PlaySound_Menu_Continue(SND_BACKGROUND_INFO);
 #endif
-
-    DoAnimation();
   }
 
+  DoAnimation();
   BackToFront();
 }
 
@@ -1537,10 +1536,12 @@ static void HandleChooseTree(int mx, int my, int dx, int dy, int button,
     }
   }
 
-  BackToFront();
-
+#if 0
   if (game_status == GAME_MODE_LEVELS || game_status == GAME_MODE_SETUP)
     DoAnimation();
+
+  BackToFront();
+#endif
 }
 
 void DrawChooseLevel()
@@ -1553,6 +1554,9 @@ void DrawChooseLevel()
 void HandleChooseLevel(int mx, int my, int dx, int dy, int button)
 {
   HandleChooseTree(mx, my, dx, dy, button, &leveldir_current);
+
+  DoAnimation();
+  BackToFront();
 }
 
 void DrawHallOfFame(int highlight_position)
@@ -1606,6 +1610,8 @@ static void drawHallOfFameList(int first_entry, int highlight_position)
       DrawText(mSX + dx2, sy, highscore[entry].Name, font_nr2);
     DrawText(mSX + dx3, sy, int2str(highscore[entry].Score, 5), font_nr4);
   }
+
+  redraw_mask |= REDRAW_FIELD;
 }
 
 void HandleHallOfFame(int mx, int my, int dx, int dy, int button)
@@ -1636,8 +1642,6 @@ void HandleHallOfFame(int mx, int my, int dx, int dy, int button)
        first_entry = 0;
 
       drawHallOfFameList(first_entry, highlight_position);
-
-      return;
     }
   }
   else if (dy > 0)
@@ -1649,27 +1653,22 @@ void HandleHallOfFame(int mx, int my, int dx, int dy, int button)
        first_entry = MAX(0, MAX_SCORE_ENTRIES - NUM_MENU_ENTRIES_ON_SCREEN);
 
       drawHallOfFameList(first_entry, highlight_position);
-
-      return;
     }
   }
-
-  if (button_released)
+  else if (button_released)
   {
     FadeSound(SND_BACKGROUND_SCORES);
     game_status = GAME_MODE_MAIN;
     DrawMainMenu();
   }
 
-  BackToFront();
-
-  if (game_status == GAME_MODE_SCORES)
-  {
-    DoAnimation();
 #if 1
+  if (game_status == GAME_MODE_SCORES)
     PlaySound_Menu_Continue(SND_BACKGROUND_SCORES);
 #endif
-  }
+
+  DoAnimation();
+  BackToFront();
 }
 
 
@@ -1814,6 +1813,8 @@ static struct TokenInfo setup_info_editor[] =
   { TYPE_SWITCH,       &setup.editor.el_custom,        "Custom:"       },
   { TYPE_SWITCH,       &setup.editor.el_custom_more,   "More Custom:"  },
   { TYPE_EMPTY,                NULL,                   ""                      },
+  { TYPE_SWITCH,       &setup.editor.el_headlines,     "Headlines:"    },
+  { TYPE_EMPTY,                NULL,                   ""                      },
   { TYPE_LEAVE_MENU,   execSetupMain,          "Back"                  },
   { 0,                 NULL,                   NULL                    }
 };
@@ -1914,8 +1915,8 @@ static Key getSetupKey()
       }
     }
 
-    BackToFront();
     DoAnimation();
+    BackToFront();
 
     /* don't eat all CPU time */
     Delay(10);
@@ -2163,10 +2164,12 @@ void HandleSetupScreen_Generic(int mx, int my, int dx, int dy, int button)
     }
   }
 
+#if 0
   BackToFront();
 
   if (game_status == GAME_MODE_SETUP)
     DoAnimation();
+#endif
 }
 
 void DrawSetupScreen_Input()
@@ -2343,7 +2346,11 @@ void HandleSetupScreen_Input(int mx, int my, int dx, int dy, int button)
     static unsigned long delay = 0;
 
     if (!DelayReached(&delay, GADGET_FRAME_DELAY))
+#if 1
+      return;
+#else
       goto out;
+#endif
 
     player_nr = (player_nr + (x == 10 ? -1 : +1) + MAX_PLAYERS) % MAX_PLAYERS;
 
@@ -2407,12 +2414,14 @@ void HandleSetupScreen_Input(int mx, int my, int dx, int dy, int button)
     }
   }
 
+#if 0
   BackToFront();
 
   out:
 
   if (game_status == GAME_MODE_SETUP)
     DoAnimation();
+#endif
 }
 
 void CustomizeKeyboard(int player_nr)
@@ -2530,8 +2539,8 @@ void CustomizeKeyboard(int player_nr)
       }
     }
 
-    BackToFront();
     DoAnimation();
+    BackToFront();
 
     /* don't eat all CPU time */
     Delay(10);
@@ -2693,8 +2702,8 @@ static boolean CalibrateJoystickMain(int player_nr)
 
     }
 
-    BackToFront();
     DoAnimation();
+    BackToFront();
 
     /* don't eat all CPU time */
     Delay(10);
@@ -2771,6 +2780,9 @@ void HandleSetupScreen(int mx, int my, int dx, int dy, int button)
     HandleChooseTree(mx, my, dx, dy, button, &artwork.mus_current);
   else
     HandleSetupScreen_Generic(mx, my, dx, dy, button);
+
+  DoAnimation();
+  BackToFront();
 }
 
 void HandleGameActions()
@@ -2909,6 +2921,7 @@ static void CreateScreenScrollbuttons()
                      GDI_STATE, GD_BUTTON_UNPRESSED,
                      GDI_DESIGN_UNPRESSED, gd_bitmap_unpressed, gd_x1, gd_y1,
                      GDI_DESIGN_PRESSED, gd_bitmap_pressed, gd_x2, gd_y2,
+                     GDI_DIRECT_DRAW, FALSE,
                      GDI_EVENT_MASK, event_mask,
                      GDI_CALLBACK_ACTION, HandleScreenGadgets,
                      GDI_END);
@@ -2985,6 +2998,7 @@ static void CreateScreenScrollbars()
                      GDI_DESIGN_UNPRESSED, gd_bitmap_unpressed, gd_x1, gd_y1,
                      GDI_DESIGN_PRESSED, gd_bitmap_pressed, gd_x2, gd_y2,
                      GDI_BORDER_SIZE, SC_BORDER_SIZE, SC_BORDER_SIZE,
+                     GDI_DIRECT_DRAW, FALSE,
                      GDI_EVENT_MASK, event_mask,
                      GDI_CALLBACK_ACTION, HandleScreenGadgets,
                      GDI_END);
index 45d37436a325dec9840a38644e2fba606f3d4208..1f0856dcc7b8c3949e8c9b737f9a08ab1602ec15 100644 (file)
@@ -599,6 +599,10 @@ void DrawPlayer(struct PlayerInfo *player)
            player->is_moving ? ACTION_MOVING :
            player->snapped ? ACTION_SNAPPING : ACTION_DEFAULT);
 
+#if 0
+  printf("::: '%s'\n", element_action_info[action].suffix);
+#endif
+
   InitPlayerGfxAnimation(player, action, move_dir);
 
   /* ----------------------------------------------------------------------- */
@@ -1200,7 +1204,11 @@ static void DrawLevelFieldCrumbledSandExt(int x, int y, int graphic, int frame)
   int sx = SCREENX(x), sy = SCREENY(y);
   int element;
   int width, height, cx, cy, i;
+#if 1
+  int crumbled_border_size = graphic_info[graphic].border_size;
+#else
   int snip = TILEX / 8;        /* number of border pixels from "crumbled graphic" */
+#endif
   static int xy[4][2] =
   {
     { 0, -1 },
@@ -1243,17 +1251,17 @@ static void DrawLevelFieldCrumbledSandExt(int x, int y, int graphic, int frame)
 
       if (i == 1 || i == 2)
       {
-       width = snip;
+       width = crumbled_border_size;
        height = TILEY;
-       cx = (i == 2 ? TILEX - snip : 0);
+       cx = (i == 2 ? TILEX - crumbled_border_size : 0);
        cy = 0;
       }
       else
       {
        width = TILEX;
-       height = snip;
+       height = crumbled_border_size;
        cx = 0;
-       cy = (i == 3 ? TILEY - snip : 0);
+       cy = (i == 3 ? TILEY - crumbled_border_size : 0);
       }
 
       BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
@@ -1281,17 +1289,17 @@ static void DrawLevelFieldCrumbledSandExt(int x, int y, int graphic, int frame)
 
       if (i == 1 || i == 2)
       {
-       width = snip;
+       width = crumbled_border_size;
        height = TILEY;
-       cx = (i == 1 ? TILEX - snip : 0);
+       cx = (i == 1 ? TILEX - crumbled_border_size : 0);
        cy = 0;
       }
       else
       {
        width = TILEX;
-       height = snip;
+       height = crumbled_border_size;
        cx = 0;
-       cy = (i==0 ? TILEY-snip : 0);
+       cy = (i == 0 ? TILEY - crumbled_border_size : 0);
       }
 
       BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
@@ -1367,12 +1375,12 @@ static int getBorderElement(int x, int y)
     { EL_STEELWALL,                    EL_INVISIBLE_STEELWALL             }
   };
   int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
-  int steel_position = (x == -1 && y == -1                     ? 0 :
-                       x == lev_fieldx && y == -1              ? 1 :
-                       x == -1 && y == lev_fieldy              ? 2 :
-                       x == lev_fieldx && y == lev_fieldy      ? 3 :
-                       x == -1 || x == lev_fieldx              ? 4 :
-                       y == -1 || y == lev_fieldy              ? 5 : 6);
+  int steel_position = (x == -1                && y == -1              ? 0 :
+                       x == lev_fieldx && y == -1              ? 1 :
+                       x == -1         && y == lev_fieldy      ? 2 :
+                       x == lev_fieldx && y == lev_fieldy      ? 3 :
+                       x == -1         || x == lev_fieldx      ? 4 :
+                       y == -1         || y == lev_fieldy      ? 5 : 6);
 
   return border[steel_position][steel_type];
 }
@@ -1521,6 +1529,259 @@ void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
     DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
 }
 
+#if 1
+void DrawEnvelopeBackground(int startx, int starty, int x, int y,
+                           int xsize, int ysize, int font_nr)
+{
+  int font_width  = getFontWidth(font_nr);
+  int font_height = getFontHeight(font_nr);
+  int graphic = IMG_GAME_ENVELOPE_BACKGROUND;
+  Bitmap *src_bitmap;
+  int src_x, src_y;
+  int dst_x = SX + startx + x * font_width;
+  int dst_y = SY + starty + y * font_height;
+  int width  = graphic_info[graphic].width;
+  int height = graphic_info[graphic].height;
+  int inner_width  = MAX(width  - 2 * font_width,  font_width);
+  int inner_height = MAX(height - 2 * font_height, font_height);
+  int inner_sx = (width >= 3 * font_width ? font_width : 0);
+  int inner_sy = (height >= 3 * font_height ? font_height : 0);
+  boolean draw_masked = graphic_info[graphic].draw_masked;
+
+  getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
+
+  if (src_bitmap == NULL || width < font_width || height < font_height)
+  {
+    ClearRectangle(drawto, dst_x, dst_y, font_width, font_height);
+    return;
+  }
+
+  src_x += (x == 0 ? 0 : x == xsize - 1 ? width  - font_width  :
+           inner_sx + (x - 1) * font_width  % inner_width);
+  src_y += (y == 0 ? 0 : y == ysize - 1 ? height - font_height :
+           inner_sy + (y - 1) * font_height % inner_height);
+
+  if (draw_masked)
+  {
+    SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
+                 dst_x - src_x, dst_y - src_y);
+    BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, font_width, font_height,
+                    dst_x, dst_y);
+  }
+  else
+    BlitBitmap(src_bitmap, drawto, src_x, src_y, font_width, font_height,
+              dst_x, dst_y);
+}
+
+#else
+
+void DrawEnvelopeBackground(int dst_x, int dst_y, int ex, int ey, int font_nr)
+{
+  int font_width = getFontWidth(font_nr);
+  int font_height = getFontHeight(font_nr);
+  int graphic = IMG_GAME_ENVELOPE_BACKGROUND;
+  Bitmap *src_bitmap;
+  int src_x, src_y;
+  int width = graphic_info[graphic].width;
+  int height = graphic_info[graphic].height;
+  boolean draw_masked = graphic_info[graphic].draw_masked;
+
+  getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
+
+  if (src_bitmap == NULL)
+  {
+    ClearRectangle(drawto, dst_x, dst_y, font_width, font_height);
+    return;
+  }
+
+  src_x += (ex == -1 ? 0 : ex == +1 ? width  - font_width  : font_width);
+  src_y += (ey == -1 ? 0 : ey == +1 ? height - font_height : font_height);
+
+  if (draw_masked)
+  {
+    SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
+                 dst_x - src_x, dst_y - src_y);
+    BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, font_width, font_height,
+                    dst_x, dst_y);
+  }
+  else
+    BlitBitmap(src_bitmap, drawto, src_x, src_y, font_width, font_height,
+              dst_x, dst_y);
+}
+#endif
+
+void ShowEnvelope()
+{
+  int graphic = IMG_GAME_ENVELOPE_BACKGROUND;
+  boolean draw_masked = graphic_info[graphic].draw_masked;
+  int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_ON_BACKGROUND);
+  int font_nr = FONT_TEXT_1;
+  int font_width = getFontWidth(font_nr);
+  int font_height = getFontHeight(font_nr);
+  boolean ffwd_delay = (tape.playing && tape.fast_forward);
+  int anim_delay = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
+  int wait_delay = (ffwd_delay ? 500 : 1000);
+  int i, x, y;
+
+  /* open envelope window horizontally */
+  for (i = 0; i <= level.envelope_xsize; i++)
+  {
+    int xsize = i + 2;
+    int ysize = 2;
+    int startx = (SXSIZE - xsize * font_width)  / 2;
+    int starty = (SYSIZE - ysize * font_height) / 2;
+
+    SetDrawtoField(DRAW_BUFFERED);
+
+    BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
+
+    SetDrawtoField(DRAW_BACKBUFFER);
+
+#if 1
+    for (y=0; y < ysize; y++) for (x=0; x < xsize; x++)
+      DrawEnvelopeBackground(startx, starty, x, y, xsize, ysize, font_nr);
+#else
+    for (y=0; y < ysize; y++) for (x=0; x < xsize; x++)
+    {
+      int sx = SX + startx + x * font_width;
+      int sy = SY + starty + y * font_height;
+      int ex = (x == 0 ? -1 : x == xsize - 1 ? +1 : 0);
+      int ey = (y == 0 ? -1 : y == ysize - 1 ? +1 : 0);
+
+      DrawEnvelopeBackground(sx, sy, ex, ey, font_nr);
+    }
+#endif
+
+    redraw_mask |= REDRAW_FIELD | REDRAW_FROM_BACKBUFFER;
+    BackToFront();
+
+    Delay(anim_delay);
+  }
+
+  /* open envelope window vertically */
+  for (i = 0; i <= level.envelope_ysize; i++)
+  {
+    int xsize = level.envelope_xsize + 2;
+    int ysize = i + 2;
+    int startx = (SXSIZE - xsize * font_width)  / 2;
+    int starty = (SYSIZE - ysize * font_height) / 2;
+
+    SetDrawtoField(DRAW_BUFFERED);
+
+    BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
+
+    SetDrawtoField(DRAW_BACKBUFFER);
+
+#if 1
+    for (y=0; y < ysize; y++) for (x=0; x < xsize; x++)
+      DrawEnvelopeBackground(startx, starty, x, y, xsize, ysize, font_nr);
+#else
+    for (y=0; y < ysize; y++) for (x=0; x < xsize; x++)
+    {
+      int sx = SX + startx + x * font_width;
+      int sy = SY + starty + y * font_height;
+      int ex = (x == 0 ? -1 : x == xsize - 1 ? +1 : 0);
+      int ey = (y == 0 ? -1 : y == ysize - 1 ? +1 : 0);
+
+      DrawEnvelopeBackground(sx, sy, ex, ey, font_nr);
+    }
+#endif
+
+    DrawTextToTextArea(SX + startx + font_width,
+                      SY + starty + font_height, level.envelope,
+                      FONT_TEXT_1, level.envelope_xsize, i, mask_mode);
+
+    redraw_mask |= REDRAW_FIELD | REDRAW_FROM_BACKBUFFER;
+    BackToFront();
+
+    Delay(anim_delay);
+  }
+
+  if (tape.playing)
+    Delay(wait_delay);
+  else
+    WaitForEventToContinue();
+
+  /* close envelope window vertically */
+  for (i = level.envelope_ysize; i >= 0; i--)
+  {
+    int xsize = level.envelope_xsize + 2;
+    int ysize = i + 2;
+    int startx = (SXSIZE - xsize * font_width)  / 2;
+    int starty = (SYSIZE - ysize * font_height) / 2;
+
+    SetDrawtoField(DRAW_BUFFERED);
+
+    BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
+
+    SetDrawtoField(DRAW_BACKBUFFER);
+
+#if 1
+    for (y=0; y < ysize; y++) for (x=0; x < xsize; x++)
+      DrawEnvelopeBackground(startx, starty, x, y, xsize, ysize, font_nr);
+#else
+    for (y=0; y < ysize; y++) for (x=0; x < xsize; x++)
+    {
+      int sx = SX + startx + x * font_width;
+      int sy = SY + starty + y * font_height;
+      int ex = (x == 0 ? -1 : x == xsize - 1 ? +1 : 0);
+      int ey = (y == 0 ? -1 : y == ysize - 1 ? +1 : 0);
+
+      DrawEnvelopeBackground(sx, sy, ex, ey, font_nr);
+    }
+#endif
+
+    DrawTextToTextArea(SX + startx + font_width,
+                      SY + starty + font_height, level.envelope,
+                      FONT_TEXT_1, level.envelope_xsize, i, mask_mode);
+
+    redraw_mask |= REDRAW_FIELD | REDRAW_FROM_BACKBUFFER;
+    BackToFront();
+
+    Delay(anim_delay);
+  }
+
+  /* close envelope window horizontally */
+  for (i = level.envelope_xsize; i >= 0; i--)
+  {
+    int xsize = i + 2;
+    int ysize = 2;
+    int startx = (SXSIZE - xsize * font_width)  / 2;
+    int starty = (SYSIZE - ysize * font_height) / 2;
+
+    SetDrawtoField(DRAW_BUFFERED);
+
+    BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
+
+    SetDrawtoField(DRAW_BACKBUFFER);
+
+#if 1
+    for (y=0; y < ysize; y++) for (x=0; x < xsize; x++)
+      DrawEnvelopeBackground(startx, starty, x, y, xsize, ysize, font_nr);
+#else
+    for (y=0; y < ysize; y++) for (x=0; x < xsize; x++)
+    {
+      int sx = SX + startx + x * font_width;
+      int sy = SY + starty + y * font_height;
+      int ex = (x == 0 ? -1 : x == xsize - 1 ? +1 : 0);
+      int ey = (y == 0 ? -1 : y == ysize - 1 ? +1 : 0);
+
+      DrawEnvelopeBackground(sx, sy, ex, ey, font_nr);
+    }
+#endif
+
+    redraw_mask |= REDRAW_FIELD | REDRAW_FROM_BACKBUFFER;
+    BackToFront();
+
+    Delay(anim_delay);
+  }
+
+  SetDrawtoField(DRAW_BUFFERED);
+
+  redraw_mask |= REDRAW_FIELD;
+  BackToFront();
+}
+
 void getMicroGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
 {
   Bitmap *src_bitmap = graphic_info[graphic].bitmap;
@@ -1751,16 +2012,50 @@ void DrawMicroLevel(int xpos, int ypos, boolean restart)
   game_status = last_game_status;      /* restore current game status */
 }
 
-int REQ_in_range(int x, int y)
+void WaitForEventToContinue()
 {
-  if (y > DY+249 && y < DY+278)
+  boolean still_wait = TRUE;
+
+  /* simulate releasing mouse button over last gadget, if still pressed */
+  if (button_status)
+    HandleGadgets(-1, -1, 0);
+
+  button_status = MB_RELEASED;
+
+  while (still_wait)
   {
-    if (x > DX+1 && x < DX+48)
-      return 1;
-    else if (x > DX+51 && x < DX+98) 
-      return 2;
+    if (PendingEvent())
+    {
+      Event event;
+
+      NextEvent(&event);
+
+      switch (event.type)
+      {
+       case EVENT_BUTTONPRESS:
+       case EVENT_KEYPRESS:
+         still_wait = FALSE;
+         break;
+
+       case EVENT_KEYRELEASE:
+         ClearPlayerAction();
+         break;
+
+       default:
+         HandleOtherEvents(&event);
+         break;
+      }
+    }
+    else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
+    {
+      still_wait = FALSE;
+    }
+
+    DoAnimation();
+
+    /* don't eat all CPU time */
+    Delay(10);
   }
-  return 0;
 }
 
 #define MAX_REQUEST_LINES              13
@@ -1772,6 +2067,10 @@ boolean Request(char *text, unsigned int req_state)
   unsigned int old_door_state;
   int last_game_status = game_status;  /* save current game status */
 
+#if 1
+  SetMouseCursor(CURSOR_DEFAULT);
+#endif
+
 #if defined(PLATFORM_UNIX)
   /* pause network game while waiting for request to answer */
   if (options.network &&
@@ -1882,7 +2181,9 @@ boolean Request(char *text, unsigned int req_state)
 
   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
 
+#if 0
   SetMouseCursor(CURSOR_DEFAULT);
+#endif
 
   while(result < 0)
   {
index e7a65872bcf2593d3e54ce884610504637990567..f226a9cfd09a40ae8e2a1c83e1104d0a8728bb15 100644 (file)
@@ -113,12 +113,15 @@ void DrawLevelField(int, int);
 void DrawMiniElement(int, int, int);
 void DrawMiniElementOrWall(int, int, int, int);
 
+void ShowEnvelope();
+
 void getMicroGraphicSource(int, Bitmap **, int *, int *);
 void DrawMicroElement(int, int, int);
 void DrawLevel(void);
 void DrawMiniLevel(int, int, int, int);
 void DrawMicroLevel(int, int, boolean);
 
+void WaitForEventToContinue();
 boolean Request(char *, unsigned int);
 unsigned int OpenDoor(unsigned int);
 unsigned int CloseDoor(unsigned int);