[rocksndiamonds] 03/08: New upstream version 4.0.0.2+dfsg

Stephen Kitt skitt at moszumanska.debian.org
Sat May 6 18:24:33 UTC 2017


This is an automated email from the git hooks/post-receive script.

skitt pushed a commit to branch master
in repository rocksndiamonds.

commit 09c4fb1fb029a6ca358a75daa8a866d598b8549d
Author: Stephen Kitt <skitt at debian.org>
Date:   Sat May 6 18:31:56 2017 +0200

    New upstream version 4.0.0.2+dfsg
---
 Makefile              |   7 -
 src/Makefile          |   9 +-
 src/conftime.h        |   2 +-
 src/editor.c          | 777 ++++++++++++++++++++++++++++++++------------------
 src/files.c           | 200 ++++++++-----
 src/files.h           |   2 +
 src/game.c            |  86 +++---
 src/init.c            |  26 +-
 src/libgame/gadgets.c |   2 +-
 src/libgame/misc.c    |  26 +-
 src/libgame/misc.h    |   1 +
 src/libgame/sdl.c     |  35 ++-
 src/libgame/system.c  |   8 +
 src/libgame/system.h  |   8 +
 src/main.h            |   5 +-
 src/screens.c         |  67 ++++-
 src/tape.c            |  91 +++++-
 src/tape.h            |   2 +
 src/tools.c           |  10 +
 19 files changed, 915 insertions(+), 449 deletions(-)

diff --git a/Makefile b/Makefile
index 3e113fd..5d94cb1 100644
--- a/Makefile
+++ b/Makefile
@@ -38,13 +38,6 @@ MAKE = make
 # uncomment if system has no joystick include file
 # JOYSTICK = -DNO_JOYSTICK
 
-# choose if more than one global score file entry for one player is allowed
-# (default: MANY_PER_NAME)
-# uncomment to install game in multi-user environment
-# SCORE_ENTRIES = ONE_PER_NAME
-# uncomment to install game in single-user environment (default)
-# SCORE_ENTRIES = MANY_PER_NAME
-
 # path for cross-compiling (only needed for non-native Windows build)
 CROSS_PATH_WIN32 = /usr/local/cross-tools/i386-mingw32msvc
 
diff --git a/src/Makefile b/src/Makefile
index bb33ce8..bacc168 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -97,15 +97,10 @@ ifdef RW_GAME_DIR			# path to writable game data specified
 CONFIG_RW_GAME_DIR = -DRW_GAME_DIR="\"$(RW_GAME_DIR)\""
 endif
 
-ifdef SCORE_ENTRIES			# number of score entries per player
-CONFIG_SCORE_ENTRIES = -D$(SCORE_ENTRIES)
-endif
-
-CONFIG_GAME_DIR = $(CONFIG_RO_GAME_DIR) $(CONFIG_RW_GAME_DIR)
-CONFIG_GAME = $(CONFIG_GAME_DIR) $(CONFIG_SCORE_ENTRIES)
+CONFIG = $(CONFIG_RO_GAME_DIR) $(CONFIG_RW_GAME_DIR) $(JOYSTICK)
 
-CONFIG = $(CONFIG_GAME) $(JOYSTICK)
 DEBUG = -DDEBUG -g
+
 # PROFILING = $(PROFILING_FLAGS)
 
 # OPTIONS = $(DEBUG) -Wall			# only for debugging purposes
diff --git a/src/conftime.h b/src/conftime.h
index af3e285..e4e2471 100644
--- a/src/conftime.h
+++ b/src/conftime.h
@@ -1 +1 @@
-#define SOURCE_DATE_STRING "2017-01-10 23:06"
+#define SOURCE_DATE_STRING "2017-03-15 20:24"
diff --git a/src/editor.c b/src/editor.c
index 3518185..ca104b8 100644
--- a/src/editor.c
+++ b/src/editor.c
@@ -97,27 +97,31 @@
 #define ED_TABBUTTON_XSIZE	     (graphic_info[IMG_EDITOR_TABBUTTON].width)
 #define ED_TABBUTTON_YSIZE	     (graphic_info[IMG_EDITOR_TABBUTTON].height)
 
-#define ED_LEVEL_SETTINGS_TABS_X	(editor.settings.tabs.x)
-#define ED_LEVEL_SETTINGS_TABS_Y	(editor.settings.tabs.y)
-#define ED_ELEMENT_SETTINGS_TABS_X	(editor.settings.tabs.x)
-#define ED_ELEMENT_SETTINGS_TABS_Y	(editor.settings.tabs.y +	\
+#define ED_SETTINGS_LEVEL_TABS_X	(editor.settings.tabs.x)
+#define ED_SETTINGS_LEVEL_TABS_Y	(editor.settings.tabs.y)
+#define ED_SETTINGS_ELEMENT_TABS_X	(editor.settings.tabs.x)
+#define ED_SETTINGS_ELEMENT_TABS_Y	(editor.settings.tabs.y +	\
 					 editor.settings.tabs.yoffset2)
 
 #define ED_SETTINGS_TABS_XOFFSET	(editor.settings.tabs.draw_xoffset)
 #define ED_SETTINGS_TABS_YOFFSET	(editor.settings.tabs.draw_yoffset)
 
-#define ED_LEVEL_SETTINGS_XSTART	(ED_LEVEL_SETTINGS_TABS_X +	\
+#define ED_LEVEL_TABS_XSTART		(ED_SETTINGS_LEVEL_TABS_X)
+#define ED_LEVEL_TABS_YSTART		(ED_SETTINGS_LEVEL_TABS_Y)
+#define ED_LEVEL_SETTINGS_XSTART	(ED_SETTINGS_LEVEL_TABS_X +	\
 					 ED_SETTINGS_TABS_XOFFSET)
-#define ED_LEVEL_SETTINGS_YSTART	(ED_LEVEL_SETTINGS_TABS_Y +	\
+#define ED_LEVEL_SETTINGS_YSTART	(ED_SETTINGS_LEVEL_TABS_Y +	\
 					 ED_TABBUTTON_YSIZE +		\
 					 ED_GADGET_TINY_DISTANCE +	\
 					 ED_TAB_BAR_HEIGHT +		\
 					 ED_SETTINGS_TABS_YOFFSET +	\
 					 getFontHeight(FONT_TEXT_1) +	\
 					 ED_GADGET_TEXT_DISTANCE)
-#define ED_ELEMENT_SETTINGS_XSTART	(ED_ELEMENT_SETTINGS_TABS_X +	\
+#define ED_ELEMENT_TABS_XSTART		(ED_SETTINGS_ELEMENT_TABS_X)
+#define ED_ELEMENT_TABS_YSTART		(ED_SETTINGS_ELEMENT_TABS_Y)
+#define ED_ELEMENT_SETTINGS_XSTART	(ED_SETTINGS_ELEMENT_TABS_X +	\
 					 ED_SETTINGS_TABS_XOFFSET)
-#define ED_ELEMENT_SETTINGS_YSTART	(ED_ELEMENT_SETTINGS_TABS_Y +	\
+#define ED_ELEMENT_SETTINGS_YSTART	(ED_SETTINGS_ELEMENT_TABS_Y +	\
 					 ED_TABBUTTON_YSIZE +		\
 					 ED_GADGET_TINY_DISTANCE +	\
 					 ED_TAB_BAR_HEIGHT +		\
@@ -128,62 +132,99 @@
 #define ED_SETTINGS_YOFFSET		(ED_CHECKBUTTON_YSIZE +		\
 					 ED_GADGET_LINE_DISTANCE)
 
-#define ED_POS_LEVEL_SETTINGS_RANGE	(10000)
-#define ED_POS_LEVEL_SETTINGS_FIRST	(1 * ED_POS_LEVEL_SETTINGS_RANGE)
-#define ED_POS_LEVEL_SETTINGS_LAST	(2 * ED_POS_LEVEL_SETTINGS_RANGE - 1)
-#define ED_POS_ELEMENT_SETTINGS_FIRST	(2 * ED_POS_LEVEL_SETTINGS_RANGE)
-#define ED_POS_ELEMENT_SETTINGS_LAST	(3 * ED_POS_LEVEL_SETTINGS_RANGE - 1)
+#define ED_POS_RANGE			(10000)
+#define ED_POS_LEVEL_TABS_FIRST		(1 * ED_POS_RANGE)
+#define ED_POS_LEVEL_TABS_LAST		(2 * ED_POS_RANGE - 1)
+#define ED_POS_LEVEL_SETTINGS_FIRST	(2 * ED_POS_RANGE)
+#define ED_POS_LEVEL_SETTINGS_LAST	(3 * ED_POS_RANGE - 1)
+#define ED_POS_ELEMENT_TABS_FIRST	(3 * ED_POS_RANGE)
+#define ED_POS_ELEMENT_TABS_LAST	(4 * ED_POS_RANGE - 1)
+#define ED_POS_ELEMENT_SETTINGS_FIRST	(4 * ED_POS_RANGE)
+#define ED_POS_ELEMENT_SETTINGS_LAST	(5 * ED_POS_RANGE - 1)
+
+#define ED_LEVEL_TABS_XPOS(n)		(ED_POS_LEVEL_TABS_FIRST + (n))
+#define ED_LEVEL_TABS_YPOS(n)		(ED_POS_LEVEL_TABS_FIRST + (n))
 
 #define ED_LEVEL_SETTINGS_XPOS(n)	(ED_POS_LEVEL_SETTINGS_FIRST + (n))
 #define ED_LEVEL_SETTINGS_YPOS(n)	(ED_POS_LEVEL_SETTINGS_FIRST + (n))
 
+#define ED_ELEMENT_TABS_XPOS(n)		(ED_POS_ELEMENT_TABS_FIRST + (n))
+#define ED_ELEMENT_TABS_YPOS(n)		(ED_POS_ELEMENT_TABS_FIRST + (n))
+
 #define ED_ELEMENT_SETTINGS_XPOS(n)	(ED_POS_ELEMENT_SETTINGS_FIRST + (n))
 #define ED_ELEMENT_SETTINGS_YPOS(n)	(ED_POS_ELEMENT_SETTINGS_FIRST + (n))
 
+#define IS_POS_LEVEL_TABS(n)	      ((n) >= ED_POS_LEVEL_TABS_FIRST && \
+				       (n) <= ED_POS_LEVEL_TABS_LAST)
 #define IS_POS_LEVEL_SETTINGS(n)      ((n) >= ED_POS_LEVEL_SETTINGS_FIRST && \
 				       (n) <= ED_POS_LEVEL_SETTINGS_LAST)
+#define IS_POS_ELEMENT_TABS(n)	      ((n) >= ED_POS_ELEMENT_TABS_FIRST && \
+				       (n) <= ED_POS_ELEMENT_TABS_LAST)
 #define IS_POS_ELEMENT_SETTINGS(n)    ((n) >= ED_POS_ELEMENT_SETTINGS_FIRST && \
 				       (n) <= ED_POS_ELEMENT_SETTINGS_LAST)
 
+#define ED_LEVEL_TABS_LINE(n)		((n) - ED_POS_LEVEL_TABS_FIRST)
 #define ED_LEVEL_SETTINGS_LINE(n)	((n) - ED_POS_LEVEL_SETTINGS_FIRST)
+#define ED_ELEMENT_TABS_LINE(n)		((n) - ED_POS_ELEMENT_TABS_FIRST)
 #define ED_ELEMENT_SETTINGS_LINE(n)	((n) - ED_POS_ELEMENT_SETTINGS_FIRST)
 
+#define ED_LEVEL_TABS_X(n)		(ED_LEVEL_TABS_XSTART +	\
+					 (n) * ED_SETTINGS_TABS_XOFFSET)
+#define ED_LEVEL_TABS_Y(n)		(ED_LEVEL_TABS_YSTART +	\
+					 (n) * ED_SETTINGS_TABS_YOFFSET)
+
 #define ED_LEVEL_SETTINGS_X(n)		(ED_LEVEL_SETTINGS_XSTART +	\
 					 (n) * ED_SETTINGS_XOFFSET)
 #define ED_LEVEL_SETTINGS_Y(n)		(ED_LEVEL_SETTINGS_YSTART +	\
 					 (n) * ED_SETTINGS_YOFFSET)
 
+#define ED_ELEMENT_TABS_X(n)		(ED_ELEMENT_TABS_XSTART +	\
+					 (n) * ED_SETTINGS_TABS_XOFFSET)
+#define ED_ELEMENT_TABS_Y(n)		(ED_ELEMENT_TABS_YSTART +	\
+					 (n) * ED_SETTINGS_TABS_YOFFSET)
+
 #define ED_ELEMENT_SETTINGS_X(n)	(ED_ELEMENT_SETTINGS_XSTART +	\
 					 (n) * ED_SETTINGS_XOFFSET)
 #define ED_ELEMENT_SETTINGS_Y(n)	(ED_ELEMENT_SETTINGS_YSTART +	\
 					 (n) * ED_SETTINGS_YOFFSET)
 
+#define ED_POS_TO_LEVEL_TABS_X(n)	\
+  (ED_LEVEL_TABS_X(ED_LEVEL_TABS_LINE(n)))
+#define ED_POS_TO_LEVEL_TABS_Y(n)	\
+  (ED_LEVEL_TABS_Y(ED_LEVEL_TABS_LINE(n)))
+
 #define ED_POS_TO_LEVEL_SETTINGS_X(n)	\
   (ED_LEVEL_SETTINGS_X(ED_LEVEL_SETTINGS_LINE(n)))
 #define ED_POS_TO_LEVEL_SETTINGS_Y(n)	\
   (ED_LEVEL_SETTINGS_Y(ED_LEVEL_SETTINGS_LINE(n)))
 
+#define ED_POS_TO_ELEMENT_TABS_X(n)	\
+  (ED_ELEMENT_TABS_X(ED_ELEMENT_TABS_LINE(n)))
+#define ED_POS_TO_ELEMENT_TABS_Y(n)	\
+  (ED_ELEMENT_TABS_Y(ED_ELEMENT_TABS_LINE(n)))
+
 #define ED_POS_TO_ELEMENT_SETTINGS_X(n)	\
   (ED_ELEMENT_SETTINGS_X(ED_ELEMENT_SETTINGS_LINE(n)))
 #define ED_POS_TO_ELEMENT_SETTINGS_Y(n)	\
   (ED_ELEMENT_SETTINGS_Y(ED_ELEMENT_SETTINGS_LINE(n)))
 
-#define ED_SETTINGS_X(n)		(IS_POS_LEVEL_SETTINGS(n) ?	\
+#define ED_SETTINGS_X(n)		(IS_POS_LEVEL_TABS(n) ?	\
+					 ED_POS_TO_LEVEL_TABS_X(n) : \
+					 IS_POS_LEVEL_SETTINGS(n) ?	\
 					 ED_POS_TO_LEVEL_SETTINGS_X(n) : \
+					 IS_POS_ELEMENT_TABS(n) ?	\
+					 ED_POS_TO_ELEMENT_TABS_X(n) : \
 					 IS_POS_ELEMENT_SETTINGS(n) ?	\
 					 ED_POS_TO_ELEMENT_SETTINGS_X(n) : (n))
-#define ED_SETTINGS_Y(n)		(IS_POS_LEVEL_SETTINGS(n) ?	\
+#define ED_SETTINGS_Y(n)		(IS_POS_LEVEL_TABS(n) ?	\
+					 ED_POS_TO_LEVEL_TABS_Y(n) : \
+					 IS_POS_LEVEL_SETTINGS(n) ?	\
 					 ED_POS_TO_LEVEL_SETTINGS_Y(n) : \
+					 IS_POS_ELEMENT_TABS(n) ?	\
+					 ED_POS_TO_ELEMENT_TABS_Y(n) : \
 					 IS_POS_ELEMENT_SETTINGS(n) ?	\
 					 ED_POS_TO_ELEMENT_SETTINGS_Y(n) : (n))
 
-#define ED_TAB_SETTINGS_X(n)		(IS_POS_LEVEL_SETTINGS(n) ?	\
-					 ED_LEVEL_SETTINGS_TABS_X :	\
-					 ED_ELEMENT_SETTINGS_TABS_X)
-#define ED_TAB_SETTINGS_Y(n)		(IS_POS_LEVEL_SETTINGS(n) ?	\
-					 ED_LEVEL_SETTINGS_TABS_Y :	\
-					 ED_ELEMENT_SETTINGS_TABS_Y)
-
 #define ED_SETTINGS_XOFF(n)		(5 * ((n) % 4) *		\
 					 ED_DRAWINGAREA_TILE_SIZE)
 #define ED_SETTINGS_YOFF(n)		(5 * ((n) / 4) *		\
@@ -553,12 +594,13 @@
 #define GADGET_ID_PROPERTIES_CONFIG_1	(GADGET_ID_TEXTBUTTON_FIRST + 4)
 #define GADGET_ID_PROPERTIES_CONFIG_2	(GADGET_ID_TEXTBUTTON_FIRST + 5)
 #define GADGET_ID_PROPERTIES_CHANGE	(GADGET_ID_TEXTBUTTON_FIRST + 6)
-#define GADGET_ID_SAVE_AS_TEMPLATE	(GADGET_ID_TEXTBUTTON_FIRST + 7)
-#define GADGET_ID_ADD_CHANGE_PAGE	(GADGET_ID_TEXTBUTTON_FIRST + 8)
-#define GADGET_ID_DEL_CHANGE_PAGE	(GADGET_ID_TEXTBUTTON_FIRST + 9)
+#define GADGET_ID_SAVE_AS_TEMPLATE_1	(GADGET_ID_TEXTBUTTON_FIRST + 7)
+#define GADGET_ID_SAVE_AS_TEMPLATE_2	(GADGET_ID_TEXTBUTTON_FIRST + 8)
+#define GADGET_ID_ADD_CHANGE_PAGE	(GADGET_ID_TEXTBUTTON_FIRST + 9)
+#define GADGET_ID_DEL_CHANGE_PAGE	(GADGET_ID_TEXTBUTTON_FIRST + 10)
 
 /* graphicbutton identifiers */
-#define GADGET_ID_GRAPHICBUTTON_FIRST	(GADGET_ID_TEXTBUTTON_FIRST + 10)
+#define GADGET_ID_GRAPHICBUTTON_FIRST	(GADGET_ID_TEXTBUTTON_FIRST + 11)
 
 #define GADGET_ID_PREV_CHANGE_PAGE	(GADGET_ID_GRAPHICBUTTON_FIRST + 0)
 #define GADGET_ID_NEXT_CHANGE_PAGE	(GADGET_ID_GRAPHICBUTTON_FIRST + 1)
@@ -630,19 +672,21 @@
 #define GADGET_ID_CUSTOM_GRAV_REACHABLE	(GADGET_ID_CHECKBUTTON_FIRST + 42)
 #define GADGET_ID_CUSTOM_USE_LAST_VALUE	(GADGET_ID_CHECKBUTTON_FIRST + 43)
 #define GADGET_ID_CUSTOM_USE_GRAPHIC	(GADGET_ID_CHECKBUTTON_FIRST + 44)
-#define GADGET_ID_CUSTOM_USE_TEMPLATE	(GADGET_ID_CHECKBUTTON_FIRST + 45)
-#define GADGET_ID_CUSTOM_CAN_CHANGE	(GADGET_ID_CHECKBUTTON_FIRST + 46)
-#define GADGET_ID_CHANGE_USE_CONTENT	(GADGET_ID_CHECKBUTTON_FIRST + 47)
-#define GADGET_ID_CHANGE_USE_EXPLOSION	(GADGET_ID_CHECKBUTTON_FIRST + 48)
-#define GADGET_ID_CHANGE_ONLY_COMPLETE	(GADGET_ID_CHECKBUTTON_FIRST + 49)
-#define GADGET_ID_CHANGE_USE_RANDOM	(GADGET_ID_CHECKBUTTON_FIRST + 50)
-#define GADGET_ID_CHANGE_HAS_ACTION	(GADGET_ID_CHECKBUTTON_FIRST + 51)
-#define GADGET_ID_CHANGE_DELAY		(GADGET_ID_CHECKBUTTON_FIRST + 52)
-#define GADGET_ID_CHANGE_BY_DIRECT_ACT	(GADGET_ID_CHECKBUTTON_FIRST + 53)
-#define GADGET_ID_CHANGE_BY_OTHER_ACT	(GADGET_ID_CHECKBUTTON_FIRST + 54)
+#define GADGET_ID_CUSTOM_USE_TEMPLATE_1	(GADGET_ID_CHECKBUTTON_FIRST + 45)
+#define GADGET_ID_CUSTOM_USE_TEMPLATE_2	(GADGET_ID_CHECKBUTTON_FIRST + 46)
+#define GADGET_ID_CUSTOM_USE_TEMPLATE_3	(GADGET_ID_CHECKBUTTON_FIRST + 47)
+#define GADGET_ID_CUSTOM_CAN_CHANGE	(GADGET_ID_CHECKBUTTON_FIRST + 48)
+#define GADGET_ID_CHANGE_USE_CONTENT	(GADGET_ID_CHECKBUTTON_FIRST + 49)
+#define GADGET_ID_CHANGE_USE_EXPLOSION	(GADGET_ID_CHECKBUTTON_FIRST + 50)
+#define GADGET_ID_CHANGE_ONLY_COMPLETE	(GADGET_ID_CHECKBUTTON_FIRST + 51)
+#define GADGET_ID_CHANGE_USE_RANDOM	(GADGET_ID_CHECKBUTTON_FIRST + 52)
+#define GADGET_ID_CHANGE_HAS_ACTION	(GADGET_ID_CHECKBUTTON_FIRST + 53)
+#define GADGET_ID_CHANGE_DELAY		(GADGET_ID_CHECKBUTTON_FIRST + 54)
+#define GADGET_ID_CHANGE_BY_DIRECT_ACT	(GADGET_ID_CHECKBUTTON_FIRST + 55)
+#define GADGET_ID_CHANGE_BY_OTHER_ACT	(GADGET_ID_CHECKBUTTON_FIRST + 56)
 
 /* gadgets for buttons in element list */
-#define GADGET_ID_ELEMENTLIST_FIRST	(GADGET_ID_CHECKBUTTON_FIRST + 55)
+#define GADGET_ID_ELEMENTLIST_FIRST	(GADGET_ID_CHECKBUTTON_FIRST + 57)
 #define GADGET_ID_ELEMENTLIST_LAST	(GADGET_ID_ELEMENTLIST_FIRST +	\
 	 				ED_NUM_ELEMENTLIST_BUTTONS - 1)
 
@@ -801,17 +845,18 @@
 #define ED_TEXTBUTTON_ID_PROPERTIES_CONFIG_1	4
 #define ED_TEXTBUTTON_ID_PROPERTIES_CONFIG_2	5
 #define ED_TEXTBUTTON_ID_PROPERTIES_CHANGE	6
-#define ED_TEXTBUTTON_ID_SAVE_AS_TEMPLATE	7
-#define ED_TEXTBUTTON_ID_ADD_CHANGE_PAGE	8
-#define ED_TEXTBUTTON_ID_DEL_CHANGE_PAGE	9
+#define ED_TEXTBUTTON_ID_SAVE_AS_TEMPLATE_2	7
+#define ED_TEXTBUTTON_ID_SAVE_AS_TEMPLATE_1	8
+#define ED_TEXTBUTTON_ID_ADD_CHANGE_PAGE	9
+#define ED_TEXTBUTTON_ID_DEL_CHANGE_PAGE	10
 
-#define ED_NUM_TEXTBUTTONS			10
+#define ED_NUM_TEXTBUTTONS			11
 
-#define ED_TEXTBUTTON_ID_LEVELINFO_FIRST ED_TEXTBUTTON_ID_LEVELINFO_LEVEL
-#define ED_TEXTBUTTON_ID_LEVELINFO_LAST  ED_TEXTBUTTON_ID_LEVELINFO_EDITOR
+#define ED_TAB_BUTTON_ID_LEVELINFO_FIRST ED_TEXTBUTTON_ID_LEVELINFO_LEVEL
+#define ED_TAB_BUTTON_ID_LEVELINFO_LAST  ED_TEXTBUTTON_ID_LEVELINFO_EDITOR
 
-#define ED_TEXTBUTTON_ID_PROPERTIES_FIRST ED_TEXTBUTTON_ID_PROPERTIES_INFO
-#define ED_TEXTBUTTON_ID_PROPERTIES_LAST  ED_TEXTBUTTON_ID_PROPERTIES_CHANGE
+#define ED_TAB_BUTTON_ID_PROPERTIES_FIRST ED_TEXTBUTTON_ID_PROPERTIES_INFO
+#define ED_TAB_BUTTON_ID_PROPERTIES_LAST  ED_TEXTBUTTON_ID_PROPERTIES_CHANGE
 
 #define ED_TEXTBUTTON_ID_CHANGE_FIRST	ED_TEXTBUTTON_ID_ADD_CHANGE_PAGE
 #define ED_TEXTBUTTON_ID_CHANGE_LAST	ED_TEXTBUTTON_ID_DEL_CHANGE_PAGE
@@ -829,63 +874,65 @@
 
 /* values for checkbutton gadgets */
 #define ED_CHECKBUTTON_ID_RANDOM_RESTRICTED	0
-#define ED_CHECKBUTTON_ID_STICK_ELEMENT		1
-#define ED_CHECKBUTTON_ID_EM_SLIPPERY_GEMS	2
-#define ED_CHECKBUTTON_ID_EM_EXPLODES_BY_FIRE	3
-#define ED_CHECKBUTTON_ID_USE_SPRING_BUG	4
-#define ED_CHECKBUTTON_ID_USE_TIME_ORB_BUG	5
-#define ED_CHECKBUTTON_ID_RANDOM_BALL_CONTENT	6
-#define ED_CHECKBUTTON_ID_INITIAL_BALL_STATE	7
-#define ED_CHECKBUTTON_ID_GROW_INTO_DIGGABLE	8
-#define ED_CHECKBUTTON_ID_AUTO_EXIT_SOKOBAN	9
-#define ED_CHECKBUTTON_ID_CONTINUOUS_SNAPPING	10
-#define ED_CHECKBUTTON_ID_BLOCK_SNAP_FIELD	11
-#define ED_CHECKBUTTON_ID_BLOCK_LAST_FIELD	12
-#define ED_CHECKBUTTON_ID_SP_BLOCK_LAST_FIELD	13
-#define ED_CHECKBUTTON_ID_INSTANT_RELOCATION	14
-#define ED_CHECKBUTTON_ID_SHIFTED_RELOCATION	15
-#define ED_CHECKBUTTON_ID_LAZY_RELOCATION	16
-#define ED_CHECKBUTTON_ID_USE_START_ELEMENT	17
-#define ED_CHECKBUTTON_ID_USE_ARTWORK_ELEMENT	18
-#define ED_CHECKBUTTON_ID_USE_EXPLOSION_ELEMENT	19
-#define ED_CHECKBUTTON_ID_INITIAL_GRAVITY	20
-#define ED_CHECKBUTTON_ID_USE_INITIAL_INVENTORY	21
-#define ED_CHECKBUTTON_ID_CAN_PASS_TO_WALKABLE	22
-#define ED_CHECKBUTTON_ID_CAN_FALL_INTO_ACID	23
-#define ED_CHECKBUTTON_ID_CAN_MOVE_INTO_ACID	24
-#define ED_CHECKBUTTON_ID_DONT_COLLIDE_WITH	25
-#define ED_CHECKBUTTON_ID_ENVELOPE_AUTOWRAP	26
-#define ED_CHECKBUTTON_ID_ENVELOPE_CENTERED	27
-#define ED_CHECKBUTTON_ID_CUSTOM_USE_GRAPHIC	28
-#define ED_CHECKBUTTON_ID_CUSTOM_USE_TEMPLATE	29
-#define ED_CHECKBUTTON_ID_CUSTOM_ACCESSIBLE	30
-#define ED_CHECKBUTTON_ID_CUSTOM_GRAV_REACHABLE	31
-#define ED_CHECKBUTTON_ID_CUSTOM_USE_LAST_VALUE	32
-#define ED_CHECKBUTTON_ID_CUSTOM_WALK_TO_OBJECT	33
-#define ED_CHECKBUTTON_ID_CUSTOM_INDESTRUCTIBLE	34
-#define ED_CHECKBUTTON_ID_CUSTOM_CAN_MOVE	35
-#define ED_CHECKBUTTON_ID_CUSTOM_CAN_FALL	36
-#define ED_CHECKBUTTON_ID_CUSTOM_CAN_SMASH	37
-#define ED_CHECKBUTTON_ID_CUSTOM_SLIPPERY	38
-#define ED_CHECKBUTTON_ID_CUSTOM_DEADLY		39
-#define ED_CHECKBUTTON_ID_CUSTOM_CAN_EXPLODE	40
-#define ED_CHECKBUTTON_ID_CUSTOM_EXPLODE_FIRE	41
-#define ED_CHECKBUTTON_ID_CUSTOM_EXPLODE_SMASH	42
-#define ED_CHECKBUTTON_ID_CUSTOM_EXPLODE_IMPACT	43
-#define ED_CHECKBUTTON_ID_CUSTOM_CAN_CHANGE	44
-#define ED_CHECKBUTTON_ID_CHANGE_DELAY		45
-#define ED_CHECKBUTTON_ID_CHANGE_BY_DIRECT_ACT	46
-#define ED_CHECKBUTTON_ID_CHANGE_BY_OTHER_ACT	47
-#define ED_CHECKBUTTON_ID_CHANGE_USE_EXPLOSION	48
-#define ED_CHECKBUTTON_ID_CHANGE_USE_CONTENT	49
-#define ED_CHECKBUTTON_ID_CHANGE_ONLY_COMPLETE	50
-#define ED_CHECKBUTTON_ID_CHANGE_USE_RANDOM	51
-#define ED_CHECKBUTTON_ID_CHANGE_HAS_ACTION	52
-
-#define ED_NUM_CHECKBUTTONS			53
+#define ED_CHECKBUTTON_ID_CUSTOM_USE_TEMPLATE_3	1
+#define ED_CHECKBUTTON_ID_CUSTOM_USE_TEMPLATE_2	2
+#define ED_CHECKBUTTON_ID_STICK_ELEMENT		3
+#define ED_CHECKBUTTON_ID_EM_SLIPPERY_GEMS	4
+#define ED_CHECKBUTTON_ID_EM_EXPLODES_BY_FIRE	5
+#define ED_CHECKBUTTON_ID_USE_SPRING_BUG	6
+#define ED_CHECKBUTTON_ID_USE_TIME_ORB_BUG	7
+#define ED_CHECKBUTTON_ID_RANDOM_BALL_CONTENT	8
+#define ED_CHECKBUTTON_ID_INITIAL_BALL_STATE	9
+#define ED_CHECKBUTTON_ID_GROW_INTO_DIGGABLE	10
+#define ED_CHECKBUTTON_ID_AUTO_EXIT_SOKOBAN	11
+#define ED_CHECKBUTTON_ID_CONTINUOUS_SNAPPING	12
+#define ED_CHECKBUTTON_ID_BLOCK_SNAP_FIELD	13
+#define ED_CHECKBUTTON_ID_BLOCK_LAST_FIELD	14
+#define ED_CHECKBUTTON_ID_SP_BLOCK_LAST_FIELD	15
+#define ED_CHECKBUTTON_ID_INSTANT_RELOCATION	16
+#define ED_CHECKBUTTON_ID_SHIFTED_RELOCATION	17
+#define ED_CHECKBUTTON_ID_LAZY_RELOCATION	18
+#define ED_CHECKBUTTON_ID_USE_START_ELEMENT	19
+#define ED_CHECKBUTTON_ID_USE_ARTWORK_ELEMENT	20
+#define ED_CHECKBUTTON_ID_USE_EXPLOSION_ELEMENT	21
+#define ED_CHECKBUTTON_ID_INITIAL_GRAVITY	22
+#define ED_CHECKBUTTON_ID_USE_INITIAL_INVENTORY	23
+#define ED_CHECKBUTTON_ID_CAN_PASS_TO_WALKABLE	24
+#define ED_CHECKBUTTON_ID_CAN_FALL_INTO_ACID	25
+#define ED_CHECKBUTTON_ID_CAN_MOVE_INTO_ACID	26
+#define ED_CHECKBUTTON_ID_DONT_COLLIDE_WITH	27
+#define ED_CHECKBUTTON_ID_ENVELOPE_AUTOWRAP	28
+#define ED_CHECKBUTTON_ID_ENVELOPE_CENTERED	29
+#define ED_CHECKBUTTON_ID_CUSTOM_USE_GRAPHIC	30
+#define ED_CHECKBUTTON_ID_CUSTOM_USE_TEMPLATE_1	31
+#define ED_CHECKBUTTON_ID_CUSTOM_ACCESSIBLE	32
+#define ED_CHECKBUTTON_ID_CUSTOM_GRAV_REACHABLE	33
+#define ED_CHECKBUTTON_ID_CUSTOM_USE_LAST_VALUE	34
+#define ED_CHECKBUTTON_ID_CUSTOM_WALK_TO_OBJECT	35
+#define ED_CHECKBUTTON_ID_CUSTOM_INDESTRUCTIBLE	36
+#define ED_CHECKBUTTON_ID_CUSTOM_CAN_MOVE	37
+#define ED_CHECKBUTTON_ID_CUSTOM_CAN_FALL	38
+#define ED_CHECKBUTTON_ID_CUSTOM_CAN_SMASH	39
+#define ED_CHECKBUTTON_ID_CUSTOM_SLIPPERY	40
+#define ED_CHECKBUTTON_ID_CUSTOM_DEADLY		41
+#define ED_CHECKBUTTON_ID_CUSTOM_CAN_EXPLODE	42
+#define ED_CHECKBUTTON_ID_CUSTOM_EXPLODE_FIRE	43
+#define ED_CHECKBUTTON_ID_CUSTOM_EXPLODE_SMASH	44
+#define ED_CHECKBUTTON_ID_CUSTOM_EXPLODE_IMPACT	45
+#define ED_CHECKBUTTON_ID_CUSTOM_CAN_CHANGE	46
+#define ED_CHECKBUTTON_ID_CHANGE_DELAY		47
+#define ED_CHECKBUTTON_ID_CHANGE_BY_DIRECT_ACT	48
+#define ED_CHECKBUTTON_ID_CHANGE_BY_OTHER_ACT	49
+#define ED_CHECKBUTTON_ID_CHANGE_USE_EXPLOSION	50
+#define ED_CHECKBUTTON_ID_CHANGE_USE_CONTENT	51
+#define ED_CHECKBUTTON_ID_CHANGE_ONLY_COMPLETE	52
+#define ED_CHECKBUTTON_ID_CHANGE_USE_RANDOM	53
+#define ED_CHECKBUTTON_ID_CHANGE_HAS_ACTION	54
+
+#define ED_NUM_CHECKBUTTONS			55
 
 #define ED_CHECKBUTTON_ID_EDITOR_FIRST	ED_CHECKBUTTON_ID_RANDOM_RESTRICTED
-#define ED_CHECKBUTTON_ID_EDITOR_LAST	ED_CHECKBUTTON_ID_RANDOM_RESTRICTED
+#define ED_CHECKBUTTON_ID_EDITOR_LAST	ED_CHECKBUTTON_ID_CUSTOM_USE_TEMPLATE_2
 
 #define ED_CHECKBUTTON_ID_CUSTOM1_FIRST	ED_CHECKBUTTON_ID_CUSTOM_USE_GRAPHIC
 #define ED_CHECKBUTTON_ID_CUSTOM1_LAST	ED_CHECKBUTTON_ID_CUSTOM_INDESTRUCTIBLE
@@ -943,6 +990,9 @@
 
 #define ED_NUM_DRAWING_AREAS			33
 
+#define ED_DRAWING_ID_EDITOR_FIRST	ED_DRAWING_ID_RANDOM_BACKGROUND
+#define ED_DRAWING_ID_EDITOR_LAST	ED_DRAWING_ID_RANDOM_BACKGROUND
+
 
 /*
   -----------------------------------------------------------------------------
@@ -2529,68 +2579,87 @@ static struct
   int gadget_id_align;
   int size;
   char *text;
-  char *text_left, *text_right, *infotext;
+  char *text_above, *text_left, *text_right, *infotext;
 } textbutton_info[ED_NUM_TEXTBUTTONS] =
 {
+  /* ---------- level and editor settings (tabs) --------------------------- */
+
   {
-    ED_LEVEL_SETTINGS_XPOS(0),		ED_LEVEL_SETTINGS_YPOS(0),
+    ED_LEVEL_TABS_XPOS(0),		ED_LEVEL_TABS_YPOS(0),
     GADGET_ID_LEVELINFO_LEVEL,		GADGET_ID_NONE,
     8,					"Level",			
-    NULL, NULL,				"Configure level properties"
+    NULL, NULL, NULL,			"Configure level properties"
   },
   {
     -1,					-1,
     GADGET_ID_LEVELINFO_EDITOR,		GADGET_ID_LEVELINFO_LEVEL,
     8,					"Editor",			
-    NULL, NULL,				"Configure editor properties"
+    NULL, NULL, NULL,			"Configure editor properties"
   },
+
+  /* ---------- element settings (tabs) ------------------------------------ */
+
   {
-    ED_ELEMENT_SETTINGS_XPOS(0),	ED_ELEMENT_SETTINGS_YPOS(0),
+    ED_ELEMENT_TABS_XPOS(0),		ED_ELEMENT_TABS_YPOS(0),
     GADGET_ID_PROPERTIES_INFO,		GADGET_ID_NONE,
     8,					"Info",			
-    NULL, NULL,				"Show information about element"
+    NULL, NULL, NULL,			"Show information about element"
   },
   {
     -1,					-1,
     GADGET_ID_PROPERTIES_CONFIG,	GADGET_ID_PROPERTIES_INFO,
     8,					"Config",
-    NULL, NULL,				"Configure element properties"
+    NULL, NULL, NULL,			"Configure element properties"
   },
   {
     -1,					-1,
     GADGET_ID_PROPERTIES_CONFIG_1,	GADGET_ID_PROPERTIES_INFO,
     8,					"Config 1",
-    NULL, NULL,				"Configure element properties, part 1"
+    NULL, NULL, NULL,			"Configure element properties, part 1"
   },
   {
     -1,					-1,
     GADGET_ID_PROPERTIES_CONFIG_2,	GADGET_ID_PROPERTIES_CONFIG_1,
     8,					"Config 2",
-    NULL, NULL,				"Configure element properties, part 2"
+    NULL, NULL, NULL,			"Configure element properties, part 2"
   },
   {
     -1,					-1,
     GADGET_ID_PROPERTIES_CHANGE,	GADGET_ID_PROPERTIES_CONFIG_2,
     8,					"Change",
-    NULL, NULL,				"Configure custom element change pages"
+    NULL, NULL, NULL,			"Configure custom element change pages"
+  },
+
+  /* ---------- level and editor settings (buttons) ------------------------ */
+
+  {
+    ED_LEVEL_SETTINGS_XPOS(0),		ED_LEVEL_SETTINGS_YPOS(6),
+    GADGET_ID_SAVE_AS_TEMPLATE_2,	GADGET_ID_NONE,
+    -1,					"Save",
+    NULL, NULL,				"this level as level template",
+    "Save current settings as new template"
   },
+
+  /* ---------- element settings (buttons) --------------------------------- */
+
   {
     -1,					-1,
-    GADGET_ID_SAVE_AS_TEMPLATE,		GADGET_ID_CUSTOM_USE_TEMPLATE,
+    GADGET_ID_SAVE_AS_TEMPLATE_1,	GADGET_ID_CUSTOM_USE_TEMPLATE_1,
     -1,					"Save",
-    " ", "As Template",			"Save current settings as new template"
+    NULL, " ",				"As Template",
+    "Save current settings as new template"
   },
   {
     -1,					-1,
     GADGET_ID_ADD_CHANGE_PAGE,		GADGET_ID_PASTE_CHANGE_PAGE,
     -1,					"New",
-    NULL, NULL,				"Add new change page"
+    NULL, NULL, NULL,			"Add new change page"
   },
   {
     -1,					-1,
     GADGET_ID_DEL_CHANGE_PAGE,		GADGET_ID_ADD_CHANGE_PAGE,
     -1,					"Delete",
-    NULL, NULL,				"Delete current change page"
+    NULL, NULL, NULL,			"Delete current change page"
   },
 };
 
@@ -2743,7 +2812,7 @@ static struct
   int gadget_id;
   int gadget_id_align;
   boolean *value;
-  char *text_left, *text_right, *infotext;
+  char *text_above, *text_left, *text_right, *infotext;
 } checkbutton_info[ED_NUM_CHECKBUTTONS] =
 {
   /* ---------- level and editor settings ---------------------------------- */
@@ -2752,9 +2821,23 @@ static struct
     ED_LEVEL_SETTINGS_XPOS(0),		ED_LEVEL_SETTINGS_YPOS(1),
     GADGET_ID_RANDOM_RESTRICTED,	GADGET_ID_NONE,
     &random_placement_background_restricted,
-    NULL,
+    NULL, NULL,
     "restrict random placement to:",	"set random placement restriction"
   },
+  {
+    ED_LEVEL_SETTINGS_XPOS(0),		ED_LEVEL_SETTINGS_YPOS(4),
+    GADGET_ID_CUSTOM_USE_TEMPLATE_3,	GADGET_ID_NONE,
+    &setup.editor.use_template_for_new_levels,
+    "Template for new levels and CE/GE:", NULL,
+    "use template for new levels",	"use template for level properties"
+  },
+  {
+    ED_LEVEL_SETTINGS_XPOS(0),		ED_LEVEL_SETTINGS_YPOS(5),
+    GADGET_ID_CUSTOM_USE_TEMPLATE_2,	GADGET_ID_NONE,
+    &level.use_custom_template,
+    NULL, NULL,
+    "use template for custom elements",	"use template for custom properties"
+  },
 
   /* ---------- element settings: configure (various elements) ------------- */
 
@@ -2762,189 +2845,189 @@ static struct
     ED_ELEMENT_SETTINGS_XPOS(0),	ED_ELEMENT_SETTINGS_YPOS(0),
     GADGET_ID_STICK_ELEMENT,		GADGET_ID_NONE,
     &stick_element_properties_window,
-    NULL,
+    NULL, NULL,
     "stick this screen to edit content","stick this screen to edit content"
   },
   {
     ED_ELEMENT_SETTINGS_XPOS(0),	ED_ELEMENT_SETTINGS_YPOS(1),
     GADGET_ID_EM_SLIPPERY_GEMS,		GADGET_ID_NONE,
     &level.em_slippery_gems,
-    NULL,
+    NULL, NULL,
     "slip down from certain flat walls","use EM/DC style slipping behaviour"
   },
   {
     ED_ELEMENT_SETTINGS_XPOS(0),	ED_ELEMENT_SETTINGS_YPOS(1),
     GADGET_ID_EM_EXPLODES_BY_FIRE,	GADGET_ID_NONE,
     &level.em_explodes_by_fire,
-    NULL,
+    NULL, NULL,
     "explodes with chain reaction",	"use R'n'D style explosion behaviour"
   },
   {
     ED_ELEMENT_SETTINGS_XPOS(0),	ED_ELEMENT_SETTINGS_YPOS(2),
     GADGET_ID_USE_SPRING_BUG,		GADGET_ID_NONE,
     &level.use_spring_bug,
-    NULL,
+    NULL, NULL,
     "use spring pushing bug",		"use odd spring pushing behaviour"
   },
   {
     ED_ELEMENT_SETTINGS_XPOS(0),	ED_ELEMENT_SETTINGS_YPOS(1),
     GADGET_ID_USE_TIME_ORB_BUG,		GADGET_ID_NONE,
     &level.use_time_orb_bug,
-    NULL,
+    NULL, NULL,
     "use time orb bug",			"use odd time orb behaviour"
   },
   {
     ED_ELEMENT_SETTINGS_XPOS(0),	ED_ELEMENT_SETTINGS_YPOS(2),
     GADGET_ID_RANDOM_BALL_CONTENT,	GADGET_ID_NONE,
     &level.ball_random,
-    NULL,
+    NULL, NULL,
     "create single random element",	"only create one element from content"
   },
   {
     ED_ELEMENT_SETTINGS_XPOS(0),	ED_ELEMENT_SETTINGS_YPOS(1),
     GADGET_ID_INITIAL_BALL_STATE,	GADGET_ID_NONE,
     &level.ball_state_initial,
-    NULL,
+    NULL, NULL,
     "magic ball initially activated",	"activate magic ball after level start"
   },
   {
     ED_ELEMENT_SETTINGS_XPOS(0),	ED_ELEMENT_SETTINGS_YPOS(0),
     GADGET_ID_GROW_INTO_DIGGABLE,	GADGET_ID_NONE,
     &level.grow_into_diggable,
-    NULL,
+    NULL, NULL,
     "can grow into anything diggable",	"grow into more than just sand"
   },
   {
     ED_ELEMENT_SETTINGS_XPOS(0),	ED_ELEMENT_SETTINGS_YPOS(0),
     GADGET_ID_AUTO_EXIT_SOKOBAN,	GADGET_ID_NONE,
     &level.auto_exit_sokoban,
-    NULL,
+    NULL, NULL,
     "exit level if all fields solved",	"automatically finish Sokoban levels"
   },
   {
     ED_ELEMENT_SETTINGS_XPOS(0),	ED_ELEMENT_SETTINGS_YPOS(9),
     GADGET_ID_CONTINUOUS_SNAPPING,	GADGET_ID_NONE,
     &level.continuous_snapping,
-    NULL,
+    NULL, NULL,
     "continuous snapping",		"use snapping without releasing key"
   },
   {
     ED_ELEMENT_SETTINGS_XPOS(0),	ED_ELEMENT_SETTINGS_YPOS(8),
     GADGET_ID_BLOCK_SNAP_FIELD,		GADGET_ID_NONE,
     &level.block_snap_field,
-    NULL,
+    NULL, NULL,
     "block snapped field when snapping", "use snapping delay to show animation"
   },
   {
     ED_ELEMENT_SETTINGS_XPOS(0),	ED_ELEMENT_SETTINGS_YPOS(2),
     GADGET_ID_BLOCK_LAST_FIELD,		GADGET_ID_NONE,
     &level.block_last_field,
-    NULL,
+    NULL, NULL,
     "block last field when moving",	"player blocks last field when moving"
   },
   {
     ED_ELEMENT_SETTINGS_XPOS(0),	ED_ELEMENT_SETTINGS_YPOS(2),
     GADGET_ID_SP_BLOCK_LAST_FIELD,	GADGET_ID_NONE,
     &level.sp_block_last_field,
-    NULL,
+    NULL, NULL,
     "block last field when moving",	"player blocks last field when moving"
   },
   {
     ED_ELEMENT_SETTINGS_XPOS(0),	ED_ELEMENT_SETTINGS_YPOS(3),
     GADGET_ID_INSTANT_RELOCATION,	GADGET_ID_NONE,
     &level.instant_relocation,
-    NULL,
+    NULL, NULL,
     "no scrolling when relocating",	"player gets relocated without delay"
   },
   {
     ED_ELEMENT_SETTINGS_XPOS(0),	ED_ELEMENT_SETTINGS_YPOS(4),
     GADGET_ID_SHIFTED_RELOCATION,	GADGET_ID_NONE,
     &level.shifted_relocation,
-    NULL,
+    NULL, NULL,
     "no centering when relocating",	"level not centered after relocation"
   },
   {
     ED_ELEMENT_SETTINGS_XPOS(0),	ED_ELEMENT_SETTINGS_YPOS(5),
     GADGET_ID_LAZY_RELOCATION,		GADGET_ID_NONE,
     &level.lazy_relocation,
-    NULL,
+    NULL, NULL,
     "only redraw off-screen relocation","no redraw if relocation target visible"
   },
   {
     ED_ELEMENT_SETTINGS_XPOS(0),	ED_ELEMENT_SETTINGS_YPOS(10),
     GADGET_ID_USE_START_ELEMENT,	GADGET_ID_NONE,
     &level.use_start_element[0],
-    NULL,
+    NULL, NULL,
     "use level start element:",	       "start level at this element's position"
   },
   {
     ED_ELEMENT_SETTINGS_XPOS(0),	ED_ELEMENT_SETTINGS_YPOS(11),
     GADGET_ID_USE_ARTWORK_ELEMENT,	GADGET_ID_NONE,
     &level.use_artwork_element[0],
-    NULL,
+    NULL, NULL,
     "use artwork from element:",	"use player artwork from other element"
   },
   {
     ED_ELEMENT_SETTINGS_XPOS(0),	ED_ELEMENT_SETTINGS_YPOS(12),
     GADGET_ID_USE_EXPLOSION_ELEMENT,	GADGET_ID_NONE,
     &level.use_explosion_element[0],
-    NULL,
+    NULL, NULL,
     "use explosion from element:",	"use explosion properties from element"
   },
   {
     ED_ELEMENT_SETTINGS_XPOS(0),	ED_ELEMENT_SETTINGS_YPOS(13),
     GADGET_ID_INITIAL_GRAVITY,		GADGET_ID_NONE,
     &level.initial_player_gravity[0],
-    NULL,
+    NULL, NULL,
     "use initial gravity",		"set initial player gravity"
   },
   {
     ED_ELEMENT_SETTINGS_XPOS(0),	ED_ELEMENT_SETTINGS_YPOS(1),
     GADGET_ID_USE_INITIAL_INVENTORY,	GADGET_ID_NONE,
     &level.use_initial_inventory[0],
-    NULL,
+    NULL, NULL,
     "use initial inventory:",		"use collected elements on level start"
   },
   {
     ED_ELEMENT_SETTINGS_XPOS(0),	ED_ELEMENT_SETTINGS_YPOS(6),
     GADGET_ID_CAN_PASS_TO_WALKABLE,	GADGET_ID_NONE,
     &level.can_pass_to_walkable,
-    NULL,
+    NULL, NULL,
     "can pass to walkable element",	"player can pass to empty or walkable"
   },
   {
     ED_ELEMENT_SETTINGS_XPOS(0),	ED_ELEMENT_SETTINGS_YPOS(1),
     GADGET_ID_CAN_FALL_INTO_ACID,	GADGET_ID_NONE,
     &custom_element_properties[EP_CAN_MOVE_INTO_ACID],
-    NULL,
+    NULL, NULL,
     "can fall into acid (with gravity)","player can fall into acid pool"
   },
   {
     ED_ELEMENT_SETTINGS_XPOS(0),	ED_ELEMENT_SETTINGS_YPOS(0),
     GADGET_ID_CAN_MOVE_INTO_ACID,	GADGET_ID_NONE,
     &custom_element_properties[EP_CAN_MOVE_INTO_ACID],
-    NULL,
+    NULL, NULL,
     "can move into acid",		"element can move into acid pool"
   },
   {
     ED_ELEMENT_SETTINGS_XPOS(0),	ED_ELEMENT_SETTINGS_YPOS(1),
     GADGET_ID_DONT_COLLIDE_WITH,	GADGET_ID_NONE,
     &custom_element_properties[EP_DONT_COLLIDE_WITH],
-    NULL,
+    NULL, NULL,
     "deadly when colliding with",	"element is deadly when hitting player"
   },
   {
     ED_ELEMENT_SETTINGS_XPOS(0),	ED_ELEMENT_SETTINGS_YPOS(1),
     GADGET_ID_ENVELOPE_AUTOWRAP,	GADGET_ID_NONE,
     &level.envelope[0].autowrap,
-    NULL,
+    NULL, NULL,
     "auto-wrap",			"automatically wrap envelope text"
   },
   {
     -1,					ED_ELEMENT_SETTINGS_YPOS(1),
     GADGET_ID_ENVELOPE_CENTERED,	GADGET_ID_ENVELOPE_AUTOWRAP,
     &level.envelope[0].centered,
-    " ",
+    NULL, " ",
     "centered",				"automatically center envelope text"
   },
 
@@ -2954,49 +3037,49 @@ static struct
     ED_ELEMENT_SETTINGS_XPOS(0),	ED_ELEMENT_SETTINGS_YPOS(1),
     GADGET_ID_CUSTOM_USE_GRAPHIC,	GADGET_ID_NONE,
     &custom_element.use_gfx_element,
-    NULL,
+    NULL, NULL,
     "use graphic of element:",		"use existing element graphic"
   },
   {
     ED_ELEMENT_SETTINGS_XPOS(0),	ED_ELEMENT_SETTINGS_YPOS(14),
-    GADGET_ID_CUSTOM_USE_TEMPLATE,	GADGET_ID_NONE,
+    GADGET_ID_CUSTOM_USE_TEMPLATE_1,	GADGET_ID_NONE,
     &level.use_custom_template,
-    NULL,
+    NULL, NULL,
     "use template",			"use template for custom properties"
   },
   {
     ED_ELEMENT_SETTINGS_XPOS(0),	ED_ELEMENT_SETTINGS_YPOS(2),
     GADGET_ID_CUSTOM_ACCESSIBLE,	GADGET_ID_NONE,
     &custom_element_properties[EP_ACCESSIBLE],
-    NULL,
+    NULL, NULL,
     NULL,				"player can walk to or pass this field"
   },
   {
     ED_ELEMENT_SETTINGS_XPOS(0),	ED_ELEMENT_SETTINGS_YPOS(9),
     GADGET_ID_CUSTOM_GRAV_REACHABLE,	GADGET_ID_NONE,
     &custom_element_properties[EP_GRAVITY_REACHABLE],
-    NULL,
+    NULL, NULL,
     "reachable despite gravity",	"player can walk/dig despite gravity"
   },
   {
     ED_ELEMENT_SETTINGS_XPOS(0),	ED_ELEMENT_SETTINGS_YPOS(11),
     GADGET_ID_CUSTOM_USE_LAST_VALUE,	GADGET_ID_NONE,
     &custom_element.use_last_ce_value,
-    NULL,
+    NULL, NULL,
     "use last CE value after change",	"use last CE value after change"
   },
   {
     ED_ELEMENT_SETTINGS_XPOS(0),	ED_ELEMENT_SETTINGS_YPOS(4),
     GADGET_ID_CUSTOM_WALK_TO_OBJECT,	GADGET_ID_NONE,
     &custom_element_properties[EP_WALK_TO_OBJECT],
-    NULL,
+    NULL, NULL,
     NULL,				"player can dig/collect/push element"
   },
   {
     ED_ELEMENT_SETTINGS_XPOS(0),	ED_ELEMENT_SETTINGS_YPOS(8),
     GADGET_ID_CUSTOM_INDESTRUCTIBLE,	GADGET_ID_NONE,
     &custom_element_properties[EP_INDESTRUCTIBLE],
-    NULL,
+    NULL, NULL,
     "indestructible",			"element is indestructible"
   },
 
@@ -3006,63 +3089,63 @@ static struct
     ED_ELEMENT_SETTINGS_XPOS(0),	ED_ELEMENT_SETTINGS_YPOS(1),
     GADGET_ID_CUSTOM_CAN_MOVE,		GADGET_ID_NONE,
     &custom_element_properties[EP_CAN_MOVE],
-    NULL,
+    NULL, NULL,
     NULL,				"element can move with some pattern"
   },
   {
     ED_ELEMENT_SETTINGS_XPOS(0),	ED_ELEMENT_SETTINGS_YPOS(7),
     GADGET_ID_CUSTOM_CAN_FALL,		GADGET_ID_NONE,
     &custom_element_properties[EP_CAN_FALL],
-    NULL,
+    NULL, NULL,
     "can fall",				"element can fall down"
   },
   {
     -1,					ED_ELEMENT_SETTINGS_YPOS(7),
     GADGET_ID_CUSTOM_CAN_SMASH,		GADGET_ID_CUSTOM_CAN_FALL,
     &custom_element_properties[EP_CAN_SMASH],
-    " ",
+    NULL, " ",
     NULL,				"element can smash other elements"
   },
   {
     ED_ELEMENT_SETTINGS_XPOS(0),	ED_ELEMENT_SETTINGS_YPOS(8),
     GADGET_ID_CUSTOM_SLIPPERY,		GADGET_ID_NONE,
     &custom_element_properties[EP_SLIPPERY],
-    NULL,
+    NULL, NULL,
     NULL,				"other elements can fall down from it"
   },
   {
     ED_ELEMENT_SETTINGS_XPOS(0),	ED_ELEMENT_SETTINGS_YPOS(9),
     GADGET_ID_CUSTOM_DEADLY,		GADGET_ID_NONE,
     &custom_element_properties[EP_DEADLY],
-    NULL,
+    NULL, NULL,
     NULL,				"element can kill the player"
   },
   {
     ED_ELEMENT_SETTINGS_XPOS(0),	ED_ELEMENT_SETTINGS_YPOS(10),
     GADGET_ID_CUSTOM_CAN_EXPLODE,	GADGET_ID_NONE,
     &custom_element_properties[EP_CAN_EXPLODE],
-    NULL,
+    NULL, NULL,
     NULL,				"element can explode"
   },
   {
     ED_ELEMENT_SETTINGS_XPOS(1),	ED_ELEMENT_SETTINGS_YPOS(11),
     GADGET_ID_CUSTOM_EXPLODE_FIRE,	GADGET_ID_NONE,
     &custom_element_properties[EP_EXPLODES_BY_FIRE],
-    NULL,
+    NULL, NULL,
     "by fire",				"element can explode by fire/explosion"
   },
   {
     -1,					ED_ELEMENT_SETTINGS_YPOS(11),
     GADGET_ID_CUSTOM_EXPLODE_SMASH,	GADGET_ID_CUSTOM_EXPLODE_FIRE,
     &custom_element_properties[EP_EXPLODES_SMASHED],
-    " ",
+    NULL, " ",
     "smashed",				"element can explode when smashed"
   },
   {
     -1,					ED_ELEMENT_SETTINGS_YPOS(11),
     GADGET_ID_CUSTOM_EXPLODE_IMPACT,	GADGET_ID_CUSTOM_EXPLODE_SMASH,
     &custom_element_properties[EP_EXPLODES_IMPACT],
-    " ",
+    NULL, " ",
     "impact",				"element can explode on impact"
   },
 
@@ -3072,63 +3155,63 @@ static struct
     ED_ELEMENT_SETTINGS_XPOS(0),	ED_ELEMENT_SETTINGS_YPOS(1),
     GADGET_ID_CUSTOM_CAN_CHANGE,	GADGET_ID_NONE,
     &custom_element_change.can_change,
-    NULL,
+    NULL, NULL,
     "element changes to:",		"change element on specified condition"
   },
   {
     ED_ELEMENT_SETTINGS_XPOS(1),	ED_ELEMENT_SETTINGS_YPOS(2),
     GADGET_ID_CHANGE_DELAY,		GADGET_ID_NONE,
     &custom_element_change_events[CE_DELAY],
-    NULL,
+    NULL, NULL,
     NULL,				"element changes after delay"
   },
   {
     ED_ELEMENT_SETTINGS_XPOS(1),	ED_ELEMENT_SETTINGS_YPOS(4),
     GADGET_ID_CHANGE_BY_DIRECT_ACT,	GADGET_ID_NONE,
     &custom_element_change_events[CE_BY_DIRECT_ACTION],
-    NULL,
+    NULL, NULL,
     NULL,				"element changes by direct action"
   },
   {
     ED_ELEMENT_SETTINGS_XPOS(1),	ED_ELEMENT_SETTINGS_YPOS(5),
     GADGET_ID_CHANGE_BY_OTHER_ACT,	GADGET_ID_NONE,
     &custom_element_change_events[CE_BY_OTHER_ACTION],
-    NULL,
+    NULL, NULL,
     NULL,				"element changes by other element"
   },
   {
     ED_ELEMENT_SETTINGS_XPOS(1),	ED_ELEMENT_SETTINGS_YPOS(8),
     GADGET_ID_CHANGE_USE_EXPLOSION,	GADGET_ID_NONE,
     &custom_element_change.explode,
-    NULL,
+    NULL, NULL,
     "explode instead of change",	"element explodes instead of change"
   },
   {
     ED_ELEMENT_SETTINGS_XPOS(1),	ED_ELEMENT_SETTINGS_YPOS(9),
     GADGET_ID_CHANGE_USE_CONTENT,	GADGET_ID_NONE,
     &custom_element_change.use_target_content,
-    NULL,
+    NULL, NULL,
     "use extended change target:",	"element changes to more elements"
   },
   {
     ED_ELEMENT_SETTINGS_XPOS(2),	ED_ELEMENT_SETTINGS_YPOS(11),
     GADGET_ID_CHANGE_ONLY_COMPLETE,	GADGET_ID_NONE,
     &custom_element_change.only_if_complete,
-    NULL,
+    NULL, NULL,
     "replace all or nothing",		"only replace when all can be changed"
   },
   {
     ED_ELEMENT_SETTINGS_XPOS(2),	ED_ELEMENT_SETTINGS_YPOS(12),
     GADGET_ID_CHANGE_USE_RANDOM,	GADGET_ID_NONE,
     &custom_element_change.use_random_replace,
-    NULL,
+    NULL, NULL,
     NULL,				"use percentage for random replace"
   },
   {
     ED_ELEMENT_SETTINGS_XPOS(0),	ED_ELEMENT_SETTINGS_YPOS(13),
     GADGET_ID_CHANGE_HAS_ACTION,	GADGET_ID_NONE,
     &custom_element_change.has_action,
-    NULL,
+    NULL, NULL,
     NULL,				"execute action on specified condition"
   },
 };
@@ -3505,6 +3588,7 @@ static void HandleCheckbuttons(struct GadgetInfo *);
 static void HandleControlButtons(struct GadgetInfo *);
 static void HandleDrawingAreaInfo(struct GadgetInfo *);
 static void PrintEditorGadgetInfoText(struct GadgetInfo *);
+static boolean AskToCopyAndModifyLevelTemplate();
 
 static int num_editor_gadgets = 0;	/* dynamically determined */
 
@@ -4825,7 +4909,6 @@ static boolean use_el_empty = FALSE;
 static int *editor_elements = NULL;	/* dynamically allocated */
 static int num_editor_elements = 0;	/* dynamically determined */
 
-static boolean setup_editor_show_always = TRUE;
 static boolean setup_editor_cascade_never = FALSE;
 
 static int editor_hl_unused[] = { EL_EMPTY };
@@ -4848,67 +4931,67 @@ static struct
 editor_elements_info[] =
 {
   {
-    &setup_editor_show_always,
+    &setup.editor.el_classic,
     &setup_editor_cascade_never,
     &editor_hl_unused_ptr,		&num_editor_hl_unused,
     &editor_el_players_ptr,		&num_editor_el_players
   },
   {
-    &setup.editor.el_boulderdash,
+    &setup.editor.el_classic,
     &setup.editor_cascade.el_bd,
     &editor_hl_boulderdash_ptr,		&num_editor_hl_boulderdash,
     &editor_el_boulderdash_ptr,		&num_editor_el_boulderdash
   },
   {
-    &setup.editor.el_emerald_mine,
+    &setup.editor.el_classic,
     &setup.editor_cascade.el_em,
     &editor_hl_emerald_mine_ptr,	&num_editor_hl_emerald_mine,
     &editor_el_emerald_mine_ptr,	&num_editor_el_emerald_mine
   },
   {
-    &setup.editor.el_emerald_mine_club,
+    &setup.editor.el_classic,
     &setup.editor_cascade.el_emc,
     &editor_hl_emerald_mine_club_ptr,	&num_editor_hl_emerald_mine_club,
     &editor_el_emerald_mine_club_ptr,	&num_editor_el_emerald_mine_club
   },
   {
-    &setup.editor.el_more,
+    &setup.editor.el_classic,
     &setup.editor_cascade.el_rnd,
     &editor_hl_rnd_ptr,			&num_editor_hl_rnd,
     &editor_el_rnd_ptr,			&num_editor_el_rnd
   },
   {
-    &setup.editor.el_sokoban,
+    &setup.editor.el_classic,
     &setup.editor_cascade.el_sb,
     &editor_hl_sokoban_ptr,		&num_editor_hl_sokoban,
     &editor_el_sokoban_ptr,		&num_editor_el_sokoban
   },
   {
-    &setup.editor.el_supaplex,
+    &setup.editor.el_classic,
     &setup.editor_cascade.el_sp,
     &editor_hl_supaplex_ptr,		&num_editor_hl_supaplex,
     &editor_el_supaplex_ptr,		&num_editor_el_supaplex
   },
   {
-    &setup.editor.el_diamond_caves,
+    &setup.editor.el_classic,
     &setup.editor_cascade.el_dc,
     &editor_hl_diamond_caves_ptr,	&num_editor_hl_diamond_caves,
     &editor_el_diamond_caves_ptr,	&num_editor_el_diamond_caves
   },
   {
-    &setup.editor.el_dx_boulderdash,
+    &setup.editor.el_classic,
     &setup.editor_cascade.el_dx,
     &editor_hl_dx_boulderdash_ptr,	&num_editor_hl_dx_boulderdash,
     &editor_el_dx_boulderdash_ptr,	&num_editor_el_dx_boulderdash
   },
   {
-    &setup.editor.el_chars,
+    &setup.editor.el_classic,
     &setup.editor_cascade.el_chars,
     &editor_hl_chars_ptr,		&num_editor_hl_chars,
     &editor_el_chars_ptr,		&num_editor_el_chars
   },
   {
-    &setup.editor.el_steel_chars,
+    &setup.editor.el_classic,
     &setup.editor_cascade.el_steel_chars,
     &editor_hl_steel_chars_ptr,		&num_editor_hl_steel_chars,
     &editor_el_steel_chars_ptr,		&num_editor_el_steel_chars
@@ -6134,8 +6217,8 @@ static void CreateTextbuttonGadgets()
     struct GadgetInfo *gi;
     unsigned int event_mask;
     char infotext[MAX_OUTPUT_LINESIZE + 1];
-    int x = SX + ED_TAB_SETTINGS_X(textbutton_info[i].x);
-    int y = SY + ED_TAB_SETTINGS_Y(textbutton_info[i].y);
+    int x = SX + ED_SETTINGS_X(textbutton_info[i].x);
+    int y = SY + ED_SETTINGS_Y(textbutton_info[i].y);
 
     if (textbutton_info[i].size == -1)	/* dynamically determine size */
       textbutton_info[i].size = strlen(textbutton_info[i].text);
@@ -6729,9 +6812,12 @@ 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_above = font_height + ED_GADGET_LINE_DISTANCE;
   int yoffset = (gi->height - font_height) / 2;
   int x_left = gi->x - xoffset_left;
   int x_right = gi->x + gi->width + xoffset_right;
+  int y_above = gi->y - yoffset_above;
+  int x = gi->x;
   int y = gi->y + yoffset;
 
   /* only show button to delete change pages when more than minimum pages */
@@ -6739,6 +6825,9 @@ static void MapTextbuttonGadget(int id)
       custom_element.num_change_pages == MIN_CHANGE_PAGES)
     return;
 
+  if (textbutton_info[id].text_above)
+    DrawText(x, y_above, textbutton_info[id].text_above, font_nr);
+
   if (textbutton_info[id].text_left)
     DrawText(x_left, y, textbutton_info[id].text_left, font_nr);
 
@@ -6801,7 +6890,10 @@ static void MapCheckbuttonGadget(int id)
   struct GadgetInfo *gi = level_editor_gadget[checkbutton_info[id].gadget_id];
   int xoffset_left = getTextWidthForGadget(checkbutton_info[id].text_left);
   int xoffset_right = ED_GADGET_TEXT_DISTANCE;
+  int yoffset_above = font_height + ED_GADGET_LINE_DISTANCE;
   int yoffset = (gi->height - font_height) / 2;
+  int y_above = gi->y - yoffset_above;
+  int x = gi->x;
   int x_left, x_right, y;	/* set after gadget position was modified */
 
   /* set position for gadgets with dynamically determined position */
@@ -6813,6 +6905,9 @@ static void MapCheckbuttonGadget(int id)
   x_right = gi->x + gi->width + xoffset_right;
   y = gi->y + yoffset;
 
+  if (checkbutton_info[id].text_above)
+    DrawText(x, y_above, checkbutton_info[id].text_above, font_nr);
+
   if (checkbutton_info[id].text_left)
     DrawText(x_left, y, checkbutton_info[id].text_left, font_nr);
 
@@ -7280,6 +7375,8 @@ static void replace_custom_element_in_playfield(int element_from,
 static boolean CopyCustomElement(int element_old, int element_new,
 				 int copy_mode)
 {
+  int copy_mode_orig = copy_mode;
+
   if (copy_mode == GADGET_ID_CUSTOM_COPY)
   {
     element_new = (IS_CUSTOM_ELEMENT(element_old) ?
@@ -7311,6 +7408,13 @@ static boolean CopyCustomElement(int element_old, int element_new,
     level.changed = TRUE;
   }
 
+  /* when modifying custom/group element, ask for copying level template */
+  if (copy_mode_orig != GADGET_ID_CUSTOM_COPY && level.use_custom_template)
+  {
+    if (!AskToCopyAndModifyLevelTemplate())
+      return FALSE;
+  }
+
   if (copy_mode == GADGET_ID_CUSTOM_COPY_FROM)
   {
     copy_custom_element_settings(element_new, element_old);
@@ -7516,6 +7620,29 @@ static void CopyElementPropertiesToEditor(int element)
     CopyClassicElementPropertiesToEditor(element);
 }
 
+static boolean AskToCopyAndModifyLevelTemplate()
+{
+  if (Request("Copy and modify settings from level template?", REQ_ASK))
+  {
+    level.use_custom_template = FALSE;
+
+    ModifyGadget(level_editor_gadget[GADGET_ID_CUSTOM_USE_TEMPLATE_1],
+		 GDI_CHECKED, FALSE, GDI_END);
+    ModifyGadget(level_editor_gadget[GADGET_ID_CUSTOM_USE_TEMPLATE_2],
+		 GDI_CHECKED, FALSE, GDI_END);
+
+    return TRUE;
+  }
+  else
+  {
+    LoadLevelTemplate(-1);	/* this resets all element modifications ... */
+
+    DrawEditModeWindow();	/* ... and copies them to 'custom_element' */
+
+    return FALSE;
+  }
+}
+
 static void CopyCustomElementPropertiesToGame(int element)
 {
   int i;
@@ -7526,20 +7653,7 @@ static void CopyCustomElementPropertiesToGame(int element)
   level.changed = TRUE;
 
   if (level.use_custom_template)
-  {
-    if (Request("Copy and modify level template?", REQ_ASK))
-    {
-      level.use_custom_template = FALSE;
-      ModifyGadget(level_editor_gadget[GADGET_ID_CUSTOM_USE_TEMPLATE],
-		   GDI_CHECKED, FALSE, GDI_END);
-    }
-    else
-    {
-      LoadLevelTemplate(-1);	/* this resets all element modifications ... */
-
-      DrawEditModeWindow();	/* ... and copies them to 'custom_element' */
-    }
-  }
+    AskToCopyAndModifyLevelTemplate();
 
   element_info[element] = custom_element;
   *element_info[element].change = custom_element_change;
@@ -7650,12 +7764,15 @@ static void CopyCustomElementPropertiesToGame(int element)
 
 static void CopyGroupElementPropertiesToGame(int element)
 {
-  element_info[element] = custom_element;
-  *element_info[element].group = group_element_info;
-
   /* mark that this group element has been modified */
-  element_info[element].modified_settings = TRUE;
+  custom_element.modified_settings = TRUE;
   level.changed = TRUE;
+
+  if (level.use_custom_template)
+    AskToCopyAndModifyLevelTemplate();
+
+  element_info[element] = custom_element;
+  *element_info[element].group = group_element_info;
 }
 
 static void CopyClassicElementPropertiesToGame(int element)
@@ -8121,8 +8238,8 @@ static void DrawLevelInfoTabulatorGadgets()
   int gd_x = gd->x + gd_gi1->border.width / 2;
   int gd_y = gd->y + gd_gi1->height - 1;
   Pixel tab_color = GetPixel(gd->bitmap, gd_x, gd_y);
-  int id_first = ED_TEXTBUTTON_ID_LEVELINFO_LEVEL;
-  int id_last  = ED_TEXTBUTTON_ID_LEVELINFO_EDITOR;
+  int id_first = ED_TAB_BUTTON_ID_LEVELINFO_FIRST;
+  int id_last  = ED_TAB_BUTTON_ID_LEVELINFO_LAST;
   int i;
 
   for (i = id_first; i <= id_last; i++)
@@ -8235,6 +8352,9 @@ static void DrawLevelInfoEditor()
 
   /* draw drawing area */
   MapDrawingArea(ED_DRAWING_ID_RANDOM_BACKGROUND);
+
+  /* draw textbutton gadgets */
+  MapTextbuttonGadget(ED_TEXTBUTTON_ID_SAVE_AS_TEMPLATE_2);
 }
 
 static void DrawLevelInfoWindow()
@@ -8478,41 +8598,22 @@ static void DrawEnvelopeTextArea(int envelope_nr)
   MapTextAreaGadget(ED_TEXTAREA_ID_ENVELOPE_INFO);
 }
 
-static boolean PrintInfoText(char *text, int font_nr, int start_line)
+static void PrintInfoText(char *text, int font_nr, int xpos, int ypos)
 {
-  int font_height = getFontHeight(font_nr);
-  int pad_x = ED_ELEMENT_SETTINGS_X(0);
-  int pad_y = ED_ELEMENT_SETTINGS_Y(0) + ED_GADGET_SMALL_DISTANCE;
-  int sx = SX + pad_x;
-  int sy = SY + pad_y;
-  int max_lines_per_screen = (SYSIZE - pad_y) / font_height - 1;
-
-  if (start_line >= max_lines_per_screen)
-    return FALSE;
-
-  DrawText(sx, sy + start_line * font_height, text, font_nr);
-
-  return TRUE;
+  DrawText(SX + xpos, SY + ypos, text, font_nr);
 }
 
-static int PrintElementDescriptionFromFile(char *filename, int start_line)
+static int PrintElementDescriptionFromFile(char *filename, int font_nr,
+					   int xpos, int ypos)
 {
-  int font_nr = FONT_TEXT_2;
   int font_width = getFontWidth(font_nr);
   int font_height = getFontHeight(font_nr);
-  int pad_x = ED_ELEMENT_SETTINGS_X(0);
-  int pad_y = ED_ELEMENT_SETTINGS_Y(0) + ED_GADGET_SMALL_DISTANCE;
-  int sx = SX + pad_x;
-  int sy = SY + pad_y + start_line * font_height;
-  int max_chars_per_line = (SXSIZE - 2 * pad_x) / font_width;
-  int max_lines_per_screen = (SYSIZE - pad_y) / font_height - 1;
-  int max_lines_drawable = max_lines_per_screen - start_line;
-
-  if (start_line >= max_lines_per_screen)
-    return FALSE;
+  int max_chars_per_line = (SXSIZE - 2 * xpos) / font_width;
+  int max_lines_drawable = (SYSIZE - ypos) / font_height - 1;
 
-  return DrawTextFile(sx, sy, filename, font_nr, max_chars_per_line, -1,
-		      max_lines_drawable, 0, -1, TRUE, FALSE, FALSE);
+  return DrawTextFile(SX + xpos, SY + ypos, filename, font_nr,
+		      max_chars_per_line, -1, max_lines_drawable, 0, -1,
+		      TRUE, FALSE, FALSE);
 }
 
 static void DrawPropertiesInfo()
@@ -8578,23 +8679,34 @@ static void DrawPropertiesInfo()
   char *filename = getElementDescriptionFilename(properties_element);
   char *percentage_text = "In this level: ";
   char *properties_text = "Standard properties: ";
+  char *description_text = "Description:";
+  char *no_description_text = "No description available.";
+  char *none_text = "None";
   float percentage;
   int num_elements_in_level;
   int num_standard_properties = 0;
   int font1_nr = FONT_TEXT_1;
   int font2_nr = FONT_TEXT_2;
   int font1_width = getFontWidth(font1_nr);
+  int font1_height = getFontHeight(font1_nr);
   int font2_height = getFontHeight(font2_nr);
-  int pad_x = ED_ELEMENT_SETTINGS_X(0);
-  int pad_y = ED_ELEMENT_SETTINGS_Y(0) + ED_GADGET_SMALL_DISTANCE;
-  int screen_line = 0;
+  int line1_height = font1_height + ED_GADGET_LINE_DISTANCE;
+  int font2_yoffset = (font1_height - font2_height) / 2;
+  int percentage_text_len = strlen(percentage_text) * font1_width;
+  int properties_text_len = strlen(properties_text) * font1_width;
+  int xpos = ED_ELEMENT_SETTINGS_X(0);
+  int ypos = ED_ELEMENT_SETTINGS_Y(0) + ED_GADGET_SMALL_DISTANCE;
   int i, x, y;
 
   if (setup.editor.show_element_token)
   {
-    DrawTextF(pad_x, pad_y + screen_line++ * font2_height, FONT_TEXT_3,
+    int font3_nr = FONT_TEXT_3;
+    int font3_height = getFontHeight(font3_nr);
+
+    DrawTextF(xpos, ypos, font3_nr,
 	      "[%s]", element_info[properties_element].token_name);
-    screen_line++;
+
+    ypos += 2 * font3_height;
   }
 
   /* ----- print number of elements / percentage of this element in level */
@@ -8606,40 +8718,53 @@ static void DrawPropertiesInfo()
 	num_elements_in_level++;
   percentage = num_elements_in_level * 100.0 / (lev_fieldx * lev_fieldy);
 
-  DrawTextS(pad_x, pad_y + screen_line * font2_height, font1_nr,
-	    percentage_text);
-  DrawTextF(pad_x + strlen(percentage_text) * font1_width,
-	    pad_y + screen_line++ * font2_height, font2_nr,
-	    "%d (%.2f%%)", num_elements_in_level, percentage);
+  DrawTextS(xpos, ypos, font1_nr, percentage_text);
+
+  if (num_elements_in_level > 0)
+    DrawTextF(xpos + percentage_text_len, ypos + font2_yoffset, font2_nr,
+	      "%d (%.2f %%)", num_elements_in_level, percentage);
+  else
+    DrawTextF(xpos + percentage_text_len, ypos + font2_yoffset, font2_nr,
+	      none_text);
 
-  screen_line++;
+  ypos += 2 * MAX(font1_height, font2_height);
 
   /* ----- print standard properties of this element */
 
-  DrawTextS(pad_x, pad_y + screen_line++ * font2_height, font1_nr,
-	    properties_text);
+  DrawTextS(xpos, ypos, font1_nr, properties_text);
+
+  ypos += line1_height;
 
   for (i = 0; properties[i].value != -1; i++)
   {
     if (!HAS_PROPERTY(properties_element, properties[i].value))
       continue;
 
-    DrawTextS(pad_x, pad_y + screen_line++ * font2_height, font2_nr,
-	      properties[i].text);
+    DrawTextS(xpos, ypos, font2_nr, properties[i].text);
+
+    ypos += font2_height;
+
     num_standard_properties++;
   }
 
   if (num_standard_properties == 0)
-    DrawTextS(pad_x + strlen(properties_text) * font1_width,
-	      pad_y + (screen_line - 1) * font2_height, font2_nr, "none");
+  {
+    DrawTextS(xpos + properties_text_len, ypos - line1_height + font2_yoffset,
+	      font2_nr, none_text);
+
+    ypos -= (line1_height - font1_height);
+  }
 
-  screen_line++;
+  ypos += MAX(font1_height, font2_height);
 
   /* ----- print special description of this element */
 
-  PrintInfoText("Description:", FONT_TEXT_1, screen_line);
-  if (PrintElementDescriptionFromFile(filename, screen_line + 1) == 0)
-    PrintInfoText("No description available.", FONT_TEXT_1, screen_line);
+  PrintInfoText(description_text, font1_nr, xpos, ypos);
+
+  ypos += line1_height;
+
+  if (PrintElementDescriptionFromFile(filename, font2_nr, xpos, ypos) == 0)
+    PrintInfoText(no_description_text, font1_nr, xpos, ypos - line1_height);
 }
 
 #define TEXT_COLLECTING		"Score for collecting"
@@ -8802,7 +8927,11 @@ static void DrawPropertiesConfig()
 
   if (!checkPropertiesConfig(properties_element))
   {
-    PrintInfoText("No configuration options available.", FONT_TEXT_1, 0);
+    int xpos = ED_ELEMENT_SETTINGS_X(0);
+    int ypos = ED_ELEMENT_SETTINGS_Y(0) + ED_GADGET_SMALL_DISTANCE;
+
+    PrintInfoText("No configuration options available.",
+		  FONT_TEXT_1, xpos, ypos);
 
     return;
   }
@@ -9032,7 +9161,7 @@ static void DrawPropertiesConfig()
 	MapSelectboxGadget(i);
 
       /* draw textbutton gadgets */
-      MapTextbuttonGadget(ED_TEXTBUTTON_ID_SAVE_AS_TEMPLATE);
+      MapTextbuttonGadget(ED_TEXTBUTTON_ID_SAVE_AS_TEMPLATE_1);
 
       /* draw text input gadgets */
       MapTextInputGadget(ED_TEXTINPUT_ID_ELEMENT_NAME);
@@ -9072,7 +9201,7 @@ static void DrawPropertiesConfig()
 
     /* draw checkbutton gadgets */
     MapCheckbuttonGadget(ED_CHECKBUTTON_ID_CUSTOM_USE_GRAPHIC);
-    MapCheckbuttonGadget(ED_CHECKBUTTON_ID_CUSTOM_USE_TEMPLATE);
+    MapCheckbuttonGadget(ED_CHECKBUTTON_ID_CUSTOM_USE_TEMPLATE_1);
 
     /* draw counter gadgets */
     MapCounterButtons(ED_COUNTER_ID_GROUP_CONTENT);
@@ -9081,7 +9210,7 @@ static void DrawPropertiesConfig()
     MapSelectboxGadget(ED_SELECTBOX_ID_GROUP_CHOICE_MODE);
 
     /* draw textbutton gadgets */
-    MapTextbuttonGadget(ED_TEXTBUTTON_ID_SAVE_AS_TEMPLATE);
+    MapTextbuttonGadget(ED_TEXTBUTTON_ID_SAVE_AS_TEMPLATE_1);
 
     /* draw drawing area gadgets */
     DrawGroupElementArea(properties_element);
@@ -9309,11 +9438,9 @@ static void DrawPaletteWindow()
 
 static void UpdateCustomElementGraphicGadgets()
 {
-  struct ElementInfo *ei = &element_info[properties_element];
   int i;
 
-  ei->gfx_element = (ei->use_gfx_element ? ei->gfx_element_initial :
-		     properties_element);
+  InitElementPropertiesGfxElement();
 
   ModifyEditorElementList();
   RedrawDrawingElements();
@@ -10945,6 +11072,7 @@ static void HandleDrawingAreas(struct GadgetInfo *gi)
 {
   static boolean started_inside_drawing_area = FALSE;
   int id = gi->custom_id;
+  int type_id = gi->custom_type_id;
   boolean button_press_event;
   boolean button_release_event;
   boolean inside_drawing_area = !gi->event.off_borders;
@@ -11054,9 +11182,8 @@ static void HandleDrawingAreas(struct GadgetInfo *gi)
 	  SetElementButton(lx, ly, new_element, button);
 	}
       }
-      else
+      else if (!button_release_event)
       {
-	int type_id = gi->custom_type_id;
 	int pos = sx * drawingarea_info[type_id].area_ysize + sy;
 
 	if (item_xsize == MINI_TILEX && item_ysize == MINI_TILEY)
@@ -11183,17 +11310,22 @@ static void HandleDrawingAreas(struct GadgetInfo *gi)
 	PickDrawingElement(button, Feld[lx][ly]);
       else
       {
-	int type_id = gi->custom_type_id;
 	int pos = sx * drawingarea_info[type_id].area_ysize + sy;
 
 	PickDrawingElement(button, drawingarea_info[type_id].value[pos]);
       }
 
-      break;
-
     default:
       break;
   }
+
+  /* do not mark level as modified for certain non-level-changing gadgets */
+  if ((type_id >= ED_DRAWING_ID_EDITOR_FIRST &&
+       type_id <= ED_DRAWING_ID_EDITOR_LAST) ||
+      actual_drawing_function == GADGET_ID_PICK_ELEMENT)
+    return;
+
+  level.changed = TRUE;
 }
 
 static void HandleCounterButtons(struct GadgetInfo *gi)
@@ -11290,6 +11422,11 @@ static void HandleCounterButtons(struct GadgetInfo *gi)
        counter_id <= ED_COUNTER_ID_CHANGE_LAST))
     CopyElementPropertiesToGame(properties_element);
 
+  /* do not mark level as modified for certain non-level-changing gadgets */
+  if (counter_id >= ED_COUNTER_ID_EDITOR_FIRST &&
+      counter_id <= ED_COUNTER_ID_EDITOR_LAST)
+    return;
+
   level.changed = TRUE;
 }
 
@@ -11360,24 +11497,24 @@ static void HandleTextbuttonGadgets(struct GadgetInfo *gi)
   int type_id = gi->custom_type_id;
   int i;
 
-  if (type_id >= ED_TEXTBUTTON_ID_LEVELINFO_FIRST &&
-      type_id <= ED_TEXTBUTTON_ID_LEVELINFO_LAST)
+  if (type_id >= ED_TAB_BUTTON_ID_LEVELINFO_FIRST &&
+      type_id <= ED_TAB_BUTTON_ID_LEVELINFO_LAST)
   {
     edit_mode_levelinfo = gi->custom_type_id;
 
     DrawLevelInfoWindow();
   }
-  else if (type_id >= ED_TEXTBUTTON_ID_PROPERTIES_FIRST &&
-	   type_id <= ED_TEXTBUTTON_ID_PROPERTIES_LAST)
+  else if (type_id >= ED_TAB_BUTTON_ID_PROPERTIES_FIRST &&
+	   type_id <= ED_TAB_BUTTON_ID_PROPERTIES_LAST)
   {
     edit_mode_properties = gi->custom_type_id;
 
     DrawPropertiesWindow();
   }
-  else if (type_id == ED_TEXTBUTTON_ID_SAVE_AS_TEMPLATE)
+  else if (type_id == ED_TEXTBUTTON_ID_SAVE_AS_TEMPLATE_1 ||
+	   type_id == ED_TEXTBUTTON_ID_SAVE_AS_TEMPLATE_2)
   {
-    char *template_filename = getDefaultLevelFilename(-1);
-    boolean new_template = !fileExists(template_filename);
+    boolean new_template = !fileExists(getLocalLevelTemplateFilename());
 
     /* backup original "level.field" (needed to track playfield changes) */
     CopyPlayfield(level.field, FieldBackup);
@@ -11400,6 +11537,10 @@ static void HandleTextbuttonGadgets(struct GadgetInfo *gi)
   {
     struct ElementInfo *ei = &element_info[properties_element];
 
+    /* when modifying custom element, ask for copying level template */
+    if (level.use_custom_template && !AskToCopyAndModifyLevelTemplate())
+      return;
+
     setElementChangePages(ei, ei->num_change_pages + 1);
 
     /* set new change page to be new current change page */
@@ -11417,6 +11558,10 @@ static void HandleTextbuttonGadgets(struct GadgetInfo *gi)
   {
     struct ElementInfo *ei = &element_info[properties_element];
 
+    /* when modifying custom element, ask for copying level template */
+    if (level.use_custom_template && !AskToCopyAndModifyLevelTemplate())
+      return;
+
     /* 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];
@@ -11456,10 +11601,16 @@ static void HandleGraphicbuttonGadgets(struct GadgetInfo *gi)
     int current_change_page = ei->current_change_page;
 
     if (type_id == ED_GRAPHICBUTTON_ID_COPY_CHANGE_PAGE)
+    {
       element_info[EL_INTERNAL_CLIPBOARD_CHANGE].change_page[0] =
 	ei->change_page[current_change_page];
+    }
     else if (type_id == ED_GRAPHICBUTTON_ID_PASTE_CHANGE_PAGE)
     {
+      /* when modifying custom element, ask for copying level template */
+      if (level.use_custom_template && !AskToCopyAndModifyLevelTemplate())
+	return;
+
       ei->change_page[current_change_page] =
 	element_info[EL_INTERNAL_CLIPBOARD_CHANGE].change_page[0];
 
@@ -11472,8 +11623,15 @@ static void HandleGraphicbuttonGadgets(struct GadgetInfo *gi)
 
 static void HandleRadiobuttons(struct GadgetInfo *gi)
 {
-  *radiobutton_info[gi->custom_type_id].value =
-    radiobutton_info[gi->custom_type_id].checked_value;
+  int type_id = gi->custom_type_id;
+
+  *radiobutton_info[type_id].value =
+    radiobutton_info[type_id].checked_value;
+
+  /* do not mark level as modified for certain non-level-changing gadgets */
+  if (type_id >= ED_RADIOBUTTON_ID_EDITOR_FIRST &&
+      type_id <= ED_RADIOBUTTON_ID_EDITOR_LAST)
+    return;
 
   level.changed = TRUE;
 }
@@ -11491,7 +11649,7 @@ static void HandleCheckbuttons(struct GadgetInfo *gi)
 	 type_id <= ED_CHECKBUTTON_ID_CUSTOM_LAST) ||
 	(type_id >= ED_CHECKBUTTON_ID_CHANGE_FIRST &&
 	 type_id <= ED_CHECKBUTTON_ID_CHANGE_LAST)) &&
-       type_id != ED_CHECKBUTTON_ID_CUSTOM_USE_TEMPLATE))
+       type_id != ED_CHECKBUTTON_ID_CUSTOM_USE_TEMPLATE_1))
   {
     CopyElementPropertiesToGame(properties_element);
   }
@@ -11500,24 +11658,89 @@ static void HandleCheckbuttons(struct GadgetInfo *gi)
   {
     UpdateCustomElementGraphicGadgets();
   }
-  else if (type_id == ED_CHECKBUTTON_ID_CUSTOM_USE_TEMPLATE)
+  else if (type_id == ED_CHECKBUTTON_ID_CUSTOM_USE_TEMPLATE_1 ||
+	   type_id == ED_CHECKBUTTON_ID_CUSTOM_USE_TEMPLATE_2)
   {
-    char *template_filename = getDefaultLevelFilename(-1);
+    boolean template_related_changes_found = FALSE;
+    int i;
+
+    /* check if any custom or group elements have been changed */
+    for (i = 0; i < NUM_FILE_ELEMENTS; i++)
+      if ((IS_CUSTOM_ELEMENT(i) || IS_GROUP_ELEMENT(i)) &&
+	  element_info[i].modified_settings)
+	template_related_changes_found = TRUE;
 
-    if (level.use_custom_template && !fileExists(template_filename))
+    if (level.use_custom_template &&
+	!fileExists(getGlobalLevelTemplateFilename()))
     {
       Request("No level template found!", REQ_CONFIRM);
 
       level.use_custom_template = FALSE;
+
+      ModifyGadget(gi, GDI_CHECKED, FALSE, GDI_END);
+
+      return;
+    }
+
+    if (level.use_custom_template &&
+	template_related_changes_found &&
+	!Request("Discard changes and use level template?", REQ_ASK))
+    {
+      level.use_custom_template = FALSE;
+
       ModifyGadget(gi, GDI_CHECKED, FALSE, GDI_END);
 
       return;
     }
 
+    if (!level.use_custom_template &&
+	Request("Copy settings from level template?", REQ_ASK))
+    {
+      return;
+    }
+
     LoadLevelTemplate(level.use_custom_template ? -1 : level_nr);
 
     DrawEditModeWindow();
   }
+  else if (type_id == ED_CHECKBUTTON_ID_CUSTOM_USE_TEMPLATE_3)
+  {
+    if (setup.editor.use_template_for_new_levels &&
+	!fileExists(getGlobalLevelTemplateFilename()))
+    {
+      Request("No level template found!", REQ_CONFIRM);
+
+      return;
+    }
+
+    if (setup.editor.use_template_for_new_levels &&
+	level.changed &&
+	!Request("Discard level and load template?", REQ_ASK))
+    {
+      return;
+    }
+
+    if (!setup.editor.use_template_for_new_levels &&
+	level.changed &&
+	!Request("Discard level and use empty level?", REQ_ASK))
+    {
+      return;
+    }
+
+    LoadLevel(level_nr);
+    LoadScore(level_nr);
+
+    TapeErase();
+
+    ResetUndoBuffer();
+    DrawEditModeWindow();
+  }
+
+  /* do not mark level as modified for certain non-level-changing gadgets */
+  if ((type_id >= ED_CHECKBUTTON_ID_EDITOR_FIRST &&
+       type_id <= ED_CHECKBUTTON_ID_EDITOR_LAST) ||
+      type_id == ED_CHECKBUTTON_ID_STICK_ELEMENT)
+    return;
 
   level.changed = TRUE;
 }
diff --git a/src/files.c b/src/files.c
index eb33f7d..4370cba 100644
--- a/src/files.c
+++ b/src/files.c
@@ -1765,14 +1765,19 @@ static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
 }
 
 static void setLevelInfoToDefaults(struct LevelInfo *level,
-				   boolean level_info_only)
+				   boolean level_info_only,
+				   boolean reset_file_status)
 {
   setLevelInfoToDefaults_Level(level);
 
   if (!level_info_only)
     setLevelInfoToDefaults_Elements(level);
 
-  level->no_valid_file = FALSE;
+  if (reset_file_status)
+  {
+    level->no_valid_file = FALSE;
+    level->no_level_file = FALSE;
+  }
 
   level->changed = FALSE;
 }
@@ -1861,13 +1866,14 @@ static void ActivateLevelTemplate()
 
 static char *getLevelFilenameFromBasename(char *basename)
 {
-  static char *filename = NULL;
+  static char *filename[2] = { NULL, NULL };
+  int pos = (strEqual(basename, LEVELTEMPLATE_FILENAME) ? 0 : 1);
 
-  checked_free(filename);
+  checked_free(filename[pos]);
 
-  filename = getPath2(getCurrentLevelDir(), basename);
+  filename[pos] = getPath2(getCurrentLevelDir(), basename);
 
-  return filename;
+  return filename[pos];
 }
 
 static int getFileTypeFromBasename(char *basename)
@@ -1921,7 +1927,7 @@ static char *getSingleLevelBasenameExt(int nr, char *extension)
   static char basename[MAX_FILENAME_LEN];
 
   if (nr < 0)
-    sprintf(basename, "template.%s", extension);
+    sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
   else
     sprintf(basename, "%03d.%s", nr, extension);
 
@@ -2053,31 +2059,47 @@ static int getFiletypeFromID(char *filetype_id)
   return filetype;
 }
 
-static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
+char *getLocalLevelTemplateFilename()
 {
-  int nr = lfi->nr;
+  return getDefaultLevelFilename(-1);
+}
 
-  /* special case: level number is negative => check for level template file */
-  if (nr < 0)
+char *getGlobalLevelTemplateFilename()
+{
+  /* global variable "leveldir_current" must be modified in the loop below */
+  LevelDirTree *leveldir_current_last = leveldir_current;
+  char *filename = NULL;
+
+  /* check for template level in path from current to topmost tree node */
+
+  while (leveldir_current != NULL)
   {
-    /* global variable "leveldir_current" must be modified in the loop below */
-    LevelDirTree *leveldir_current_last = leveldir_current;
+    filename = getDefaultLevelFilename(-1);
 
-    /* check for template level in path from current to topmost tree node */
+    if (fileExists(filename))
+      break;
 
-    while (leveldir_current != NULL)
-    {
-      setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
-					   "template.%s", LEVELFILE_EXTENSION);
+    leveldir_current = leveldir_current->node_parent;
+  }
 
-      if (fileExists(lfi->filename))
-	break;
+  /* restore global variable "leveldir_current" modified in above loop */
+  leveldir_current = leveldir_current_last;
 
-      leveldir_current = leveldir_current->node_parent;
-    }
+  return filename;
+}
 
-    /* restore global variable "leveldir_current" modified in above loop */
-    leveldir_current = leveldir_current_last;
+static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
+{
+  int nr = lfi->nr;
+
+  /* special case: level number is negative => check for level template file */
+  if (nr < 0)
+  {
+    setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
+					 getSingleLevelBasename(-1));
+
+    /* replace local level template filename with global template filename */
+    lfi->filename = getGlobalLevelTemplateFilename();
 
     /* no fallback if template file not existing */
     return;
@@ -3164,11 +3186,26 @@ static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
   if (!(file = openFile(filename, MODE_READ)))
   {
     level->no_valid_file = TRUE;
+    level->no_level_file = TRUE;
 
-    if (!level_info_only)
-      Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
+    if (level_info_only)
+      return;
 
-    return;
+    Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
+
+    if (!setup.editor.use_template_for_new_levels)
+      return;
+
+    /* if level file not found, try to initialize level data from template */
+    filename = getGlobalLevelTemplateFilename();
+
+    if (!(file = openFile(filename, MODE_READ)))
+      return;
+
+    /* default: for empty levels, use level template for custom elements */
+    level->use_custom_template = TRUE;
+
+    level->no_valid_file = FALSE;
   }
 
   getFileChunkBE(file, chunk_name, NULL);
@@ -5858,7 +5895,7 @@ static void LoadLevelFromFileInfo(struct LevelInfo *level,
 				  boolean level_info_only)
 {
   /* always start with reliable default values */
-  setLevelInfoToDefaults(level, level_info_only);
+  setLevelInfoToDefaults(level, level_info_only, TRUE);
 
   switch (level_file_info->type)
   {
@@ -5891,11 +5928,7 @@ static void LoadLevelFromFileInfo(struct LevelInfo *level,
 
   /* if level file is invalid, restore level structure to default values */
   if (level->no_valid_file)
-  {
-    setLevelInfoToDefaults(level, level_info_only);
-
-    level->no_valid_file = TRUE;	/* but keep "no valid file" flag */
-  }
+    setLevelInfoToDefaults(level, level_info_only, FALSE);
 
   if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
     level->game_engine_type = GAME_ENGINE_TYPE_RND;
@@ -7032,7 +7065,8 @@ static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
   return chunk_size;
 }
 
-static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
+static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
+				  boolean save_as_template)
 {
   int chunk_size;
   int i;
@@ -7096,7 +7130,7 @@ static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
   }
 
   /* if not using template level, check for non-default custom/group elements */
-  if (!level->use_custom_template)
+  if (!level->use_custom_template || save_as_template)
   {
     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
     {
@@ -7132,14 +7166,14 @@ void SaveLevel(int nr)
 {
   char *filename = getDefaultLevelFilename(nr);
 
-  SaveLevelFromFilename(&level, filename);
+  SaveLevelFromFilename(&level, filename, FALSE);
 }
 
 void SaveLevelTemplate()
 {
-  char *filename = getDefaultLevelFilename(-1);
+  char *filename = getLocalLevelTemplateFilename();
 
-  SaveLevelFromFilename(&level, filename);
+  SaveLevelFromFilename(&level, filename, TRUE);
 }
 
 boolean SaveLevelChecked(int nr)
@@ -7163,7 +7197,7 @@ boolean SaveLevelChecked(int nr)
 
 void DumpLevel(struct LevelInfo *level)
 {
-  if (level->no_valid_file)
+  if (level->no_level_file || level->no_valid_file)
   {
     Error(ERR_WARN, "cannot dump -- no valid level file found");
 
@@ -7914,41 +7948,42 @@ void SaveScore(int nr)
 #define SETUP_TOKEN_TEAM_MODE			14
 #define SETUP_TOKEN_HANDICAP			15
 #define SETUP_TOKEN_SKIP_LEVELS			16
-#define SETUP_TOKEN_TIME_LIMIT			17
-#define SETUP_TOKEN_FULLSCREEN			18
-#define SETUP_TOKEN_WINDOW_SCALING_PERCENT	19
-#define SETUP_TOKEN_WINDOW_SCALING_QUALITY	20
-#define SETUP_TOKEN_SCREEN_RENDERING_MODE	21
-#define SETUP_TOKEN_ASK_ON_ESCAPE		22
-#define SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR	23
-#define SETUP_TOKEN_QUICK_SWITCH		24
-#define SETUP_TOKEN_INPUT_ON_FOCUS		25
-#define SETUP_TOKEN_PREFER_AGA_GRAPHICS		26
-#define SETUP_TOKEN_GAME_FRAME_DELAY		27
-#define SETUP_TOKEN_SP_SHOW_BORDER_ELEMENTS	28
-#define SETUP_TOKEN_SMALL_GAME_GRAPHICS		29
-#define SETUP_TOKEN_SHOW_SNAPSHOT_BUTTONS	30
-#define SETUP_TOKEN_GRAPHICS_SET		31
-#define SETUP_TOKEN_SOUNDS_SET			32
-#define SETUP_TOKEN_MUSIC_SET			33
-#define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS	34
-#define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS	35
-#define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC	36
-#define SETUP_TOKEN_VOLUME_SIMPLE		37
-#define SETUP_TOKEN_VOLUME_LOOPS		38
-#define SETUP_TOKEN_VOLUME_MUSIC		39
-#define SETUP_TOKEN_TOUCH_CONTROL_TYPE		40
-#define SETUP_TOKEN_TOUCH_MOVE_DISTANCE		41
-#define SETUP_TOKEN_TOUCH_DROP_DISTANCE		42
-
-#define NUM_GLOBAL_SETUP_TOKENS			43
+#define SETUP_TOKEN_INCREMENT_LEVELS		17
+#define SETUP_TOKEN_TIME_LIMIT			18
+#define SETUP_TOKEN_FULLSCREEN			19
+#define SETUP_TOKEN_WINDOW_SCALING_PERCENT	20
+#define SETUP_TOKEN_WINDOW_SCALING_QUALITY	21
+#define SETUP_TOKEN_SCREEN_RENDERING_MODE	22
+#define SETUP_TOKEN_ASK_ON_ESCAPE		23
+#define SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR	24
+#define SETUP_TOKEN_QUICK_SWITCH		25
+#define SETUP_TOKEN_INPUT_ON_FOCUS		26
+#define SETUP_TOKEN_PREFER_AGA_GRAPHICS		27
+#define SETUP_TOKEN_GAME_FRAME_DELAY		28
+#define SETUP_TOKEN_SP_SHOW_BORDER_ELEMENTS	29
+#define SETUP_TOKEN_SMALL_GAME_GRAPHICS		30
+#define SETUP_TOKEN_SHOW_SNAPSHOT_BUTTONS	31
+#define SETUP_TOKEN_GRAPHICS_SET		32
+#define SETUP_TOKEN_SOUNDS_SET			33
+#define SETUP_TOKEN_MUSIC_SET			34
+#define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS	35
+#define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS	36
+#define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC	37
+#define SETUP_TOKEN_VOLUME_SIMPLE		38
+#define SETUP_TOKEN_VOLUME_LOOPS		39
+#define SETUP_TOKEN_VOLUME_MUSIC		40
+#define SETUP_TOKEN_TOUCH_CONTROL_TYPE		41
+#define SETUP_TOKEN_TOUCH_MOVE_DISTANCE		42
+#define SETUP_TOKEN_TOUCH_DROP_DISTANCE		43
+
+#define NUM_GLOBAL_SETUP_TOKENS			44
 
 /* editor setup */
-#define SETUP_TOKEN_EDITOR_EL_CHARS		0
-#define SETUP_TOKEN_EDITOR_EL_STEEL_CHARS	1
-#define SETUP_TOKEN_EDITOR_EL_CUSTOM		2
-#define SETUP_TOKEN_EDITOR_EL_USER_DEFINED	3
-#define SETUP_TOKEN_EDITOR_EL_DYNAMIC		4
+#define SETUP_TOKEN_EDITOR_EL_CLASSIC		0
+#define SETUP_TOKEN_EDITOR_EL_CUSTOM		1
+#define SETUP_TOKEN_EDITOR_EL_USER_DEFINED	2
+#define SETUP_TOKEN_EDITOR_EL_DYNAMIC		3
+#define SETUP_TOKEN_EDITOR_EL_HEADLINES		4
 #define SETUP_TOKEN_EDITOR_SHOW_ELEMENT_TOKEN	5
 
 #define NUM_EDITOR_SETUP_TOKENS			6
@@ -8106,6 +8141,7 @@ static struct TokenInfo global_setup_tokens[] =
   { TYPE_SWITCH, &si.team_mode,               "team_mode"		},
   { TYPE_SWITCH, &si.handicap,                "handicap"		},
   { TYPE_SWITCH, &si.skip_levels,             "skip_levels"		},
+  { TYPE_SWITCH, &si.increment_levels,        "increment_levels"	},
   { TYPE_SWITCH, &si.time_limit,              "time_limit"		},
   { TYPE_SWITCH, &si.fullscreen,              "fullscreen"		},
   { TYPE_INTEGER,&si.window_scaling_percent,  "window_scaling_percent"	},
@@ -8136,11 +8172,11 @@ static struct TokenInfo global_setup_tokens[] =
 
 static struct TokenInfo editor_setup_tokens[] =
 {
-  { TYPE_SWITCH, &sei.el_chars,		"editor.el_chars"		},
-  { TYPE_SWITCH, &sei.el_steel_chars,	"editor.el_steel_chars"		},
+  { TYPE_SWITCH, &sei.el_classic,	"editor.el_classic"		},
   { TYPE_SWITCH, &sei.el_custom,	"editor.el_custom"		},
   { TYPE_SWITCH, &sei.el_user_defined,	"editor.el_user_defined"	},
   { TYPE_SWITCH, &sei.el_dynamic,	"editor.el_dynamic"		},
+  { TYPE_SWITCH, &sei.el_headlines,	"editor.el_headlines"		},
   { TYPE_SWITCH, &sei.show_element_token,"editor.show_element_token"	},
 };
 
@@ -8304,6 +8340,7 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
   si->team_mode = FALSE;
   si->handicap = TRUE;
   si->skip_levels = TRUE;
+  si->increment_levels = TRUE;
   si->time_limit = TRUE;
   si->fullscreen = FALSE;
   si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
@@ -8345,13 +8382,18 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
   si->editor.el_dx_boulderdash		= TRUE;
   si->editor.el_chars			= TRUE;
   si->editor.el_steel_chars		= TRUE;
+
+  si->editor.el_classic			= TRUE;
   si->editor.el_custom			= TRUE;
 
-  si->editor.el_headlines = TRUE;
-  si->editor.el_user_defined = FALSE;
-  si->editor.el_dynamic = TRUE;
+  si->editor.el_user_defined		= FALSE;
+  si->editor.el_dynamic			= TRUE;
+
+  si->editor.el_headlines		= TRUE;
+
+  si->editor.show_element_token		= FALSE;
 
-  si->editor.show_element_token = FALSE;
+  si->editor.use_template_for_new_levels = TRUE;
 
   si->shortcut.save_game	= DEFAULT_KEY_SAVE_GAME;
   si->shortcut.load_game	= DEFAULT_KEY_LOAD_GAME;
@@ -10044,7 +10086,7 @@ void ConvertLevels()
     Print("Level %03d: ", level_nr);
 
     LoadLevel(level_nr);
-    if (level.no_valid_file)
+    if (level.no_level_file || level.no_valid_file)
     {
       Print("(no level)\n");
       continue;
diff --git a/src/files.h b/src/files.h
index 1bb5380..46dcae9 100644
--- a/src/files.h
+++ b/src/files.h
@@ -30,6 +30,8 @@ void setElementChangeInfoToDefaults(struct ElementChangeInfo *);
 void copyElementInfo(struct ElementInfo *, struct ElementInfo *);
 
 char *getDefaultLevelFilename(int);
+char *getLocalLevelTemplateFilename();
+char *getGlobalLevelTemplateFilename();
 
 void LoadLevelFromFilename(struct LevelInfo *, char *);
 void LoadLevel(int);
diff --git a/src/game.c b/src/game.c
index de245df..8d2ed46 100644
--- a/src/game.c
+++ b/src/game.c
@@ -827,6 +827,14 @@ static struct GamePanelControlInfo game_panel_controls[] =
 #define DOUBLE_PLAYER_SPEED(p)	(HALVE_MOVE_DELAY( (p)->move_delay_value))
 #define HALVE_PLAYER_SPEED(p)	(DOUBLE_MOVE_DELAY((p)->move_delay_value))
 
+/* values for scroll positions */
+#define SCROLL_POSITION_X(x)	((x) < SBX_Left  + MIDPOSX ? SBX_Left :	\
+				 (x) > SBX_Right + MIDPOSX ? SBX_Right :\
+				 (x) - MIDPOSX)
+#define SCROLL_POSITION_Y(y)	((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
+				 (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
+				 (y) - MIDPOSY)
+
 /* values for other actions */
 #define MOVE_STEPSIZE_NORMAL	(TILEX / MOVE_DELAY_NORMAL_SPEED)
 #define MOVE_STEPSIZE_MIN	(1)
@@ -3896,23 +3904,13 @@ void InitGame()
       }
     }
 
-    scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
-		start_x > SBX_Right + MIDPOSX ? SBX_Right :
-		start_x - MIDPOSX);
-
-    scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
-		start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
-		start_y - MIDPOSY);
+    scroll_x = SCROLL_POSITION_X(start_x);
+    scroll_y = SCROLL_POSITION_Y(start_y);
   }
   else
   {
-    scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
-		local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
-		local_player->jx - MIDPOSX);
-
-    scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
-		local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
-		local_player->jy - MIDPOSY);
+    scroll_x = SCROLL_POSITION_X(local_player->jx);
+    scroll_y = SCROLL_POSITION_Y(local_player->jy);
   }
 
   /* !!! FIX THIS (START) !!! */
@@ -4463,7 +4461,8 @@ void GameEnd()
     SaveLevelSetup_SeriesInfo();
   }
 
-  if (level_nr < leveldir_current->last_level)
+  if (setup.increment_levels &&
+      level_nr < leveldir_current->last_level)
     raise_level = TRUE;			/* advance to next level */
 
   if ((hi_pos = NewHiScore()) >= 0) 
@@ -4496,6 +4495,7 @@ int NewHiScore()
 {
   int k, l;
   int position = -1;
+  boolean one_score_entry_per_name = !program.many_scores_per_name;
 
   LoadScore(level_nr);
 
@@ -4513,13 +4513,15 @@ int NewHiScore()
       {
 	int m = MAX_SCORE_ENTRIES - 1;
 
-#ifdef ONE_PER_NAME
-	for (l = k; l < MAX_SCORE_ENTRIES; l++)
-	  if (strEqual(setup.player_name, highscore[l].Name))
-	    m = l;
-	if (m == k)	/* player's new highscore overwrites his old one */
-	  goto put_into_list;
-#endif
+	if (one_score_entry_per_name)
+	{
+	  for (l = k; l < MAX_SCORE_ENTRIES; l++)
+	    if (strEqual(setup.player_name, highscore[l].Name))
+	      m = l;
+
+	  if (m == k)	/* player's new highscore overwrites his old one */
+	    goto put_into_list;
+	}
 
 	for (l = m; l > k; l--)
 	{
@@ -4528,22 +4530,19 @@ int NewHiScore()
 	}
       }
 
-#ifdef ONE_PER_NAME
       put_into_list:
-#endif
+
       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
       highscore[k].Score = local_player->score_final; 
       position = k;
+
       break;
     }
-
-#ifdef ONE_PER_NAME
-    else if (!strncmp(setup.player_name, highscore[k].Name,
+    else if (one_score_entry_per_name &&
+	     !strncmp(setup.player_name, highscore[k].Name,
 		      MAX_PLAYER_NAME_LEN))
       break;	/* player already there with a higher score */
-#endif
-
   }
 
   if (position >= 0) 
@@ -4924,36 +4923,21 @@ void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
   {
     /* relocation _with_ centering of screen */
 
-    new_scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
-		    x > SBX_Right + MIDPOSX ? SBX_Right :
-		    x - MIDPOSX);
-
-    new_scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
-		    y > SBY_Lower + MIDPOSY ? SBY_Lower :
-		    y - MIDPOSY);
+    new_scroll_x = SCROLL_POSITION_X(x);
+    new_scroll_y = SCROLL_POSITION_Y(y);
   }
   else
   {
     /* relocation _without_ centering of screen */
 
-    int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
-			   old_x > SBX_Right + MIDPOSX ? SBX_Right :
-			   old_x - MIDPOSX);
-
-    int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
-			   old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
-			   old_y - MIDPOSY);
-
+    int center_scroll_x = SCROLL_POSITION_X(old_x);
+    int center_scroll_y = SCROLL_POSITION_Y(old_y);
     int offset_x = x + (scroll_x - center_scroll_x);
     int offset_y = y + (scroll_y - center_scroll_y);
 
-    new_scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
-		    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
-		    offset_x - MIDPOSX);
-
-    new_scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
-		    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
-		    offset_y - MIDPOSY);
+    /* for new screen position, apply previous offset to center position */
+    new_scroll_x = SCROLL_POSITION_X(offset_x);
+    new_scroll_y = SCROLL_POSITION_Y(offset_y);
   }
 
   if (quick_relocation)
diff --git a/src/init.c b/src/init.c
index 29f4fd4..97eaf2b 100644
--- a/src/init.c
+++ b/src/init.c
@@ -219,13 +219,26 @@ void InitElementSmallImages()
   print_timestamp_done("InitElementSmallImages");
 }
 
+inline static void InitScaledImagesScaledUp(int graphic)
+{
+  struct GraphicInfo *g = &graphic_info[graphic];
+
+  ScaleImage(graphic, g->scale_up_factor);
+}
+
 void InitScaledImages()
 {
+  struct PropertyMapping *property_mapping = getImageListPropertyMapping();
+  int num_property_mappings = getImageListPropertyMappingSize();
   int i;
 
   /* scale normal images from static configuration, if not already scaled */
   for (i = 0; i < NUM_IMAGE_FILES; i++)
-    ScaleImage(i, graphic_info[i].scale_up_factor);
+    InitScaledImagesScaledUp(i);
+
+  /* scale images from dynamic configuration, if not already scaled */
+  for (i = 0; i < num_property_mappings; i++)
+    InitScaledImagesScaledUp(property_mapping[i].artwork_index);
 }
 
 void InitBitmapPointers()
@@ -1697,6 +1710,9 @@ static void InitGraphicInfo()
     src_x = graphic_info[i].src_x;
     src_y = graphic_info[i].src_y;
 
+    if (program.headless)
+      continue;
+
     if (src_x < 0 || src_y < 0 ||
 	src_x + width  > src_bitmap_width ||
 	src_y + height > src_bitmap_height)
@@ -4948,6 +4964,9 @@ void Execute_Command(char *command)
       while (*str_ptr != ' ' && *str_ptr != '\t' && *str_ptr != '\0')
 	str_ptr++;
     }
+
+    if (global.autoplay_mode == AUTOPLAY_MODE_TEST)
+      program.headless = TRUE;
   }
   else if (strPrefix(command, "convert "))
   {
@@ -4962,6 +4981,8 @@ void Execute_Command(char *command)
       *str_ptr++ = '\0';			/* terminate leveldir string */
       global.convert_level_nr = atoi(str_ptr);	/* get level_nr value */
     }
+
+    program.headless = TRUE;
   }
   else if (strPrefix(command, "create images "))
   {
@@ -5504,6 +5525,9 @@ static void InitMusic(char *identifier)
 
 static void InitArtworkDone()
 {
+  if (program.headless)
+    return;
+
   InitGlobalAnimations();
 }
 
diff --git a/src/libgame/gadgets.c b/src/libgame/gadgets.c
index 98e46a5..6b7d259 100644
--- a/src/libgame/gadgets.c
+++ b/src/libgame/gadgets.c
@@ -1758,7 +1758,7 @@ boolean HandleGadgets(int mx, int my, int button)
       new_gi->event.type = GD_EVENT_INFO_ENTERING;
       new_gi->callback_info(new_gi);
     }
-    else if (last_info_gi != NULL)
+    else if (last_info_gi != NULL && last_info_gi->mapped)
     {
       last_info_gi->event.type = GD_EVENT_INFO_LEAVING;
       last_info_gi->callback_info(last_info_gi);
diff --git a/src/libgame/misc.c b/src/libgame/misc.c
index 4ffb51c..0c0e82c 100644
--- a/src/libgame/misc.c
+++ b/src/libgame/misc.c
@@ -222,6 +222,18 @@ void Print(char *format, ...)
   va_end(ap);
 }
 
+void PrintNoLog(char *format, ...)
+{
+  FILE *file = program.log_file_default[LOG_OUT_ID];
+  va_list ap;
+
+  va_start(ap, format);
+  vfprintf(file, format, ap);
+  va_end(ap);
+
+  fflush(file);
+}
+
 void PrintLine(char *line_chars, int line_length)
 {
   int i;
@@ -709,11 +721,19 @@ static char *getProgramMainDataPath()
     char *main_data_path_old = main_data_path;
 
     // cut relative path to Mac OS X application binary directory from path
-    main_data_path_old[strlen(main_data_path_old) -
-		       strlen(main_data_binary_subdir)] = '\0';
+    main_data_path[strlen(main_data_path) -
+		   strlen(main_data_binary_subdir)] = '\0';
+
+    // cut trailing path separator from path (but not if path is root directory)
+    if (strSuffix(main_data_path, "/") && !strEqual(main_data_path, "/"))
+      main_data_path[strlen(main_data_path) - 1] = '\0';
+
+    // replace empty path with current directory
+    if (strEqual(main_data_path, ""))
+      main_data_path = ".";
 
     // add relative path to Mac OS X application resources directory to path
-    main_data_path = getPath2(main_data_path_old, main_data_files_subdir);
+    main_data_path = getPath2(main_data_path, main_data_files_subdir);
 
     free(main_data_path_old);
   }
diff --git a/src/libgame/misc.h b/src/libgame/misc.h
index 1e8f039..6c654cb 100644
--- a/src/libgame/misc.h
+++ b/src/libgame/misc.h
@@ -119,6 +119,7 @@ void printf_line(char *, int);
 void printf_line_with_prefix(char *, char *, int);
 
 void Print(char *, ...);
+void PrintNoLog(char *, ...);
 void PrintLine(char *, int);
 void PrintLineWithPrefix(char *, char *, int);
 
diff --git a/src/libgame/sdl.c b/src/libgame/sdl.c
index a11452b..e275149 100644
--- a/src/libgame/sdl.c
+++ b/src/libgame/sdl.c
@@ -439,6 +439,9 @@ boolean SDLSetNativeSurface(SDL_Surface **surface)
 #if defined(TARGET_SDL2)
 static SDL_Texture *SDLCreateTextureFromSurface(SDL_Surface *surface)
 {
+  if (program.headless)
+    return NULL;
+
   SDL_Texture *texture = SDL_CreateTextureFromSurface(sdl_renderer, surface);
 
   if (texture == NULL)
@@ -502,8 +505,11 @@ void SDLInitVideoDisplay(void)
 #endif
 }
 
-void SDLInitVideoBuffer(boolean fullscreen)
+inline static void SDLInitVideoBuffer_VideoBuffer(boolean fullscreen)
 {
+  if (program.headless)
+    return;
+
   video.window_scaling_percent = setup.window_scaling_percent;
   video.window_scaling_quality = setup.window_scaling_quality;
 
@@ -531,7 +537,10 @@ void SDLInitVideoBuffer(boolean fullscreen)
 #else
   SDL_WM_SetCaption(program.window_title, program.window_title);
 #endif
+}
 
+inline static void SDLInitVideoBuffer_DrawBuffer()
+{
   /* SDL cannot directly draw to the visible video framebuffer like X11,
      but always uses a backbuffer, which is then blitted to the visible
      video framebuffer with 'SDL_UpdateRect' (or replaced with the current
@@ -547,6 +556,16 @@ void SDLInitVideoBuffer(boolean fullscreen)
 
   /* create additional (symbolic) buffer for double-buffering */
   ReCreateBitmap(&window, video.width, video.height);
+
+  /* create dummy drawing buffer for headless mode, if needed */
+  if (program.headless)
+    ReCreateBitmap(&backbuffer, video.width, video.height);
+}
+
+void SDLInitVideoBuffer(boolean fullscreen)
+{
+  SDLInitVideoBuffer_VideoBuffer(fullscreen);
+  SDLInitVideoBuffer_DrawBuffer();
 }
 
 static boolean SDLCreateScreen(boolean fullscreen)
@@ -983,6 +1002,9 @@ void SDLRedrawWindow()
 void SDLCreateBitmapContent(Bitmap *bitmap, int width, int height,
 			    int depth)
 {
+  if (program.headless)
+    return;
+
   SDL_Surface *surface =
     SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth, 0,0,0, 0);
 
@@ -2367,6 +2389,14 @@ Bitmap *SDLLoadImage(char *filename)
   Bitmap *new_bitmap = CreateBitmapStruct();
   SDL_Surface *sdl_image_tmp;
 
+  if (program.headless)
+  {
+    /* prevent sanity check warnings at later stage */
+    new_bitmap->width = new_bitmap->height = 1;
+
+    return new_bitmap;
+  }
+
   print_timestamp_init("SDLLoadImage");
 
   print_timestamp_time(getBaseNamePtr(filename));
@@ -2456,6 +2486,9 @@ void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
 
 void SDLOpenAudio(void)
 {
+  if (program.headless)
+    return;
+
 #if !defined(TARGET_SDL2)
   if (!strEqual(setup.system.sdl_audiodriver, ARG_DEFAULT))
     SDL_putenv(getStringCat2("SDL_AUDIODRIVER=", setup.system.sdl_audiodriver));
diff --git a/src/libgame/system.c b/src/libgame/system.c
index 00e9c72..b42c7e9 100644
--- a/src/libgame/system.c
+++ b/src/libgame/system.c
@@ -96,6 +96,8 @@ void InitProgramInfo(char *argv0, char *config_filename, char *userdata_subdir,
   program.log_filename[LOG_ERR_ID] = getLogFilename(LOG_ERR_BASENAME);
   program.log_file[LOG_OUT_ID] = program.log_file_default[LOG_OUT_ID] = stdout;
   program.log_file[LOG_ERR_ID] = program.log_file_default[LOG_ERR_ID] = stderr;
+
+  program.headless = FALSE;
 }
 
 void InitScoresInfo()
@@ -388,6 +390,9 @@ void LimitScreenUpdates(boolean enable)
 
 void InitVideoDisplay(void)
 {
+  if (program.headless)
+    return;
+
   SDLInitVideoDisplay();
 #if defined(TARGET_SDL2)
   SDLSetDisplaySize();
@@ -621,6 +626,9 @@ void BlitBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap,
   int dst_x_unclipped = dst_x;
   int dst_y_unclipped = dst_y;
 
+  if (program.headless)
+    return;
+
   if (src_bitmap == NULL || dst_bitmap == NULL)
     return;
 
diff --git a/src/libgame/system.h b/src/libgame/system.h
index fb209d3..4a54de8 100644
--- a/src/libgame/system.h
+++ b/src/libgame/system.h
@@ -508,6 +508,7 @@
 #define SOUNDSINFO_FILENAME	"soundsinfo.conf"
 #define MUSICINFO_FILENAME	"musicinfo.conf"
 #define ARTWORKINFO_CACHE_FILE	"artworkinfo.cache"
+#define LEVELTEMPLATE_FILENAME	"template.level"
 #define LEVELFILE_EXTENSION	"level"
 #define TAPEFILE_EXTENSION	"tape"
 #define SCOREFILE_EXTENSION	"score"
@@ -763,6 +764,8 @@ struct ProgramInfo
 
   boolean global_scores;
   boolean many_scores_per_name;
+
+  boolean headless;
 };
 
 struct OptionInfo
@@ -968,6 +971,8 @@ struct SetupEditorInfo
   boolean el_dx_boulderdash;
   boolean el_chars;
   boolean el_steel_chars;
+
+  boolean el_classic;
   boolean el_custom;
   boolean el_user_defined;
   boolean el_dynamic;
@@ -978,6 +983,8 @@ struct SetupEditorInfo
   boolean el_by_type;
 
   boolean show_element_token;
+
+  boolean use_template_for_new_levels;
 };
 
 struct SetupEditorCascadeInfo
@@ -1087,6 +1094,7 @@ struct SetupInfo
   boolean team_mode;
   boolean handicap;
   boolean skip_levels;
+  boolean increment_levels;
   boolean time_limit;
   boolean fullscreen;
   int window_scaling_percent;
diff --git a/src/main.h b/src/main.h
index 36bc42d..12c911b 100644
--- a/src/main.h
+++ b/src/main.h
@@ -2050,14 +2050,14 @@
 #define PROGRAM_VERSION_MAJOR		4
 #define PROGRAM_VERSION_MINOR		0
 #define PROGRAM_VERSION_PATCH		0
-#define PROGRAM_VERSION_BUILD		1
+#define PROGRAM_VERSION_BUILD		2
 #define PROGRAM_VERSION_EXTRA		""
 
 #define PROGRAM_TITLE_STRING		"Rocks'n'Diamonds"
 #define PROGRAM_AUTHOR_STRING		"Holger Schemel"
 #define PROGRAM_EMAIL_STRING		"info at artsoft.org"
 #define PROGRAM_WEBSITE_STRING		"http://www.artsoft.org/"
-#define PROGRAM_COPYRIGHT_STRING	"Copyright \xa9""1995-2016 by Holger Schemel"
+#define PROGRAM_COPYRIGHT_STRING	"Copyright \xa9""1995-2017 by Holger Schemel"
 #define PROGRAM_COMPANY_STRING		"A Game by Artsoft Entertainment"
 
 #define PROGRAM_ICON_FILENAME		"RocksIcon32x32.png"
@@ -2596,6 +2596,7 @@ struct LevelInfo
   boolean use_custom_template;	/* use custom properties from template file */
 
   boolean no_valid_file;	/* set when level file missing or invalid */
+  boolean no_level_file;	/* set when falling back to level template */
 
   boolean changed;		/* set when level was changed in the editor */
 
diff --git a/src/screens.c b/src/screens.c
index a2f63bc..0f47d87 100644
--- a/src/screens.c
+++ b/src/screens.c
@@ -1849,11 +1849,7 @@ void HandleMainMenu(int mx, int my, int dx, int dy, int button)
       pos = choice + dy;
   }
 
-  if (pos == MAIN_CONTROL_LEVELS && dx != 0 && button)
-  {
-    HandleMainMenu_SelectLevel(1, (dx < 0 ? -1 : +1), NO_DIRECT_LEVEL_SELECT);
-  }
-  else if (pos == MAIN_CONTROL_FIRST_LEVEL && !button)
+  if (pos == MAIN_CONTROL_FIRST_LEVEL && !button)
   {
     HandleMainMenu_SelectLevel(MAX_LEVELS, -1, NO_DIRECT_LEVEL_SELECT);
   }
@@ -1882,6 +1878,12 @@ void HandleMainMenu(int mx, int my, int dx, int dy, int button)
 
 	choice = pos;
       }
+      else if (dx != 0)
+      {
+	if (choice != MAIN_CONTROL_INFO &&
+	    choice != MAIN_CONTROL_SETUP)
+	  HandleMainMenu_SelectLevel(1, dx, NO_DIRECT_LEVEL_SELECT);
+      }
     }
     else
     {
@@ -2449,6 +2451,27 @@ void HandleMenuScreen(int mx, int my, int dx, int dy, int button,
 
 	choice = choice_store[mode] = first_entry + y;
       }
+      else if (dx < 0)
+      {
+	PlaySound(SND_MENU_ITEM_SELECTING);
+
+	for (i = 0; menu_info[i].type != 0; i++)
+	{
+	  if (menu_info[i].type & TYPE_LEAVE_MENU)
+	  {
+	    void (*menu_callback_function)(void) = menu_info[i].value;
+
+	    FadeSetLeaveMenu();
+
+	    menu_callback_function();
+
+	    /* absolutely needed because function changes 'menu_info'! */
+	    break;
+	  }
+	}
+
+	return;
+      }
     }
     else if (!(menu_info[first_entry + y].type & TYPE_GHOSTED))
     {
@@ -3974,6 +3997,30 @@ static void HandleChooseTree(int mx, int my, int dx, int dy, int button,
 
 	ti->cl_cursor = y;
       }
+      else if (dx < 0)
+      {
+	if (game_status == GAME_MODE_SETUP)
+	{
+	  if (setup_mode == SETUP_MODE_CHOOSE_GAME_SPEED ||
+	      setup_mode == SETUP_MODE_CHOOSE_SCROLL_DELAY ||
+	      setup_mode == SETUP_MODE_CHOOSE_SNAPSHOT_MODE)
+	    execSetupGame();
+	  else if (setup_mode == SETUP_MODE_CHOOSE_WINDOW_SIZE ||
+		   setup_mode == SETUP_MODE_CHOOSE_SCALING_TYPE ||
+		   setup_mode == SETUP_MODE_CHOOSE_RENDERING)
+	    execSetupGraphics();
+	  else if (setup_mode == SETUP_MODE_CHOOSE_VOLUME_SIMPLE ||
+		   setup_mode == SETUP_MODE_CHOOSE_VOLUME_LOOPS ||
+		   setup_mode == SETUP_MODE_CHOOSE_VOLUME_MUSIC)
+	    execSetupSound();
+	  else if (setup_mode == SETUP_MODE_CHOOSE_TOUCH_CONTROL ||
+		   setup_mode == SETUP_MODE_CHOOSE_MOVE_DISTANCE ||
+		   setup_mode == SETUP_MODE_CHOOSE_DROP_DISTANCE)
+	    execSetupTouch();
+	  else
+	    execSetupArtwork();
+	}
+      }
     }
     else
     {
@@ -4095,13 +4142,13 @@ void DrawChooseLevelNr()
 
     ti->node_top = &level_number;
     ti->sort_priority = 10000 + value;
-    ti->color = (level.no_valid_file ? FC_BLUE :
+    ti->color = (level.no_level_file ? FC_BLUE :
 		 LevelStats_getSolved(i) ? FC_GREEN :
 		 LevelStats_getPlayed(i) ? FC_YELLOW : FC_RED);
 
     sprintf(identifier, "%d", value);
     sprintf(name, "%03d: %s", value,
-	    (level.no_valid_file ? "(no file)" : level.name));
+	    (level.no_level_file ? "(no file)" : level.name));
 
     setString(&ti->identifier, identifier);
     setString(&ti->name, name);
@@ -5279,9 +5326,10 @@ static struct TokenInfo setup_info_game[] =
 {
   { TYPE_SWITCH,	&setup.team_mode,	"Team-Mode (Multi-Player):" },
   { TYPE_YES_NO,	&setup.input_on_focus,	"Only Move Focussed Player:" },
+  { TYPE_SWITCH,	&setup.time_limit,	"Time Limit:"		},
   { TYPE_SWITCH,	&setup.handicap,	"Handicap:"		},
   { TYPE_SWITCH,	&setup.skip_levels,	"Skip Unsolved Levels:"	},
-  { TYPE_SWITCH,	&setup.time_limit,	"Time Limit:"		},
+  { TYPE_SWITCH,	&setup.increment_levels,"Increment Solved Levels:" },
   { TYPE_SWITCH,	&setup.autorecord,	"Auto-Record Tapes:"	},
   { TYPE_ENTER_LIST,	execSetupChooseGameSpeed, "Game Speed:"		},
   { TYPE_STRING,	&game_speed_text,	""			},
@@ -5309,9 +5357,10 @@ static struct TokenInfo setup_info_editor[] =
   { TYPE_SWITCH,	&setup.editor.el_supaplex,	"Supaplex:"	},
   { TYPE_SWITCH,	&setup.editor.el_diamond_caves,	"Diamond Caves II:" },
   { TYPE_SWITCH,	&setup.editor.el_dx_boulderdash,"DX-Boulderdash:" },
-#endif
   { TYPE_SWITCH,	&setup.editor.el_chars,		"Text Characters:" },
   { TYPE_SWITCH, &setup.editor.el_steel_chars, "Text Characters (Steel):" },
+#endif
+  { TYPE_SWITCH,	&setup.editor.el_classic,  "Classic Elements:" },
   { TYPE_SWITCH,	&setup.editor.el_custom,  "Custom & Group Elements:" },
 #if 0
   { TYPE_SWITCH,	&setup.editor.el_headlines,	"Headlines:"	},
diff --git a/src/tape.c b/src/tape.c
index 5a879d1..f0a8f02 100644
--- a/src/tape.c
+++ b/src/tape.c
@@ -401,8 +401,7 @@ void DrawCompleteVideoDisplay()
 	     gfx.vxsize, gfx.vysize, gfx.vx, gfx.vy);
 
   /* draw tape buttons (forced) */
-  UnmapTapeButtons();
-  MapTapeButtons();
+  RedrawOrRemapTapeButtons();
 
   DrawVideoDisplay(VIDEO_ALL_OFF, 0);
 
@@ -457,6 +456,44 @@ void TapeDeactivateDisplayOff(boolean redraw_display)
 
 
 /* ========================================================================= */
+/* tape logging functions                                                    */
+/* ========================================================================= */
+
+void PrintTapeReplayProgress(boolean replay_finished)
+{
+  static unsigned int counter_last = -1;
+  unsigned int counter = Counter();
+  unsigned int counter_seconds  = counter / 1000;
+
+  if (!replay_finished)
+  {
+    unsigned int counter_delay = 50;
+
+    if (counter > counter_last + counter_delay)
+    {
+      PrintNoLog("\r");
+      PrintNoLog("Level %03d [%02d:%02d]: [%02d:%02d] - playing tape ... ",
+		 level_nr, tape.length_seconds / 60, tape.length_seconds % 60,
+		 TapeTime / 60, TapeTime % 60);
+
+      counter_last = counter;
+    }
+  }
+  else
+  {
+    PrintNoLog("\r");
+    Print("Level %03d [%02d:%02d]: (%02d:%02d.%03d / %.2f %%) - %s.\n",
+	  level_nr, tape.length_seconds / 60, tape.length_seconds % 60,
+	  counter_seconds / 60, counter_seconds % 60, counter % 1000,
+	  (float)counter / tape.length_seconds / 10,
+	  tape.auto_play_level_solved ? "solved" : "NOT SOLVED");
+
+    counter_last = -1;
+  }
+}
+
+
+/* ========================================================================= */
 /* tape control functions                                                    */
 /* ========================================================================= */
 
@@ -711,6 +748,9 @@ void TapeTogglePause(boolean toggle_mode)
 
     // restart step/move snapshots after quick loading tape
     SaveEngineSnapshotToListInitial();
+
+    // do not map undo/redo buttons after quick loading tape
+    return;
   }
 
   if (setup.show_snapshot_buttons &&
@@ -852,6 +892,9 @@ byte *TapePlayAction()
     tape.delay_played = 0;
   }
 
+  if (tape.auto_play)
+    PrintTapeReplayProgress(FALSE);
+
   return action;
 }
 
@@ -1050,7 +1093,7 @@ void AutoPlayTape()
   if (autoplay_initialized)
   {
     /* just finished auto-playing tape */
-    Print("%s.\n", tape.auto_play_level_solved ? "solved" : "NOT SOLVED");
+    PrintTapeReplayProgress(TRUE);
 
     num_levels_played++;
 
@@ -1107,18 +1150,18 @@ void AutoPlayTape()
 
     TapeErase();
 
-    Print("Level %03d: ", level_nr);
-
     LoadLevel(level_nr);
-    if (level.no_valid_file)
+
+    if (level.no_level_file || level.no_valid_file)
     {
-      Print("(no level)\n");
+      Print("Level %03d: (no level)\n", level_nr);
+
       continue;
     }
 
 #if 0
     /* ACTIVATE THIS FOR LOADING/TESTING OF LEVELS ONLY */
-    Print("(only testing level)\n");
+    Print("Level %03d: (only testing level)\n", level_nr);
     continue;
 #endif
 
@@ -1131,12 +1174,12 @@ void AutoPlayTape()
     {
       num_tape_missing++;
 
-      Print("(no tape)\n");
+      Print("Level %03d: (no tape)\n", level_nr);
 
       continue;
     }
 
-    Print("playing tape ... ");
+    InitCounter();
 
     TapeStartGamePlaying();
     TapeStartWarpForward(global.autoplay_mode);
@@ -1290,6 +1333,34 @@ void UnmapTapeButtons()
     UnmapGameButtons();
 }
 
+void RedrawTapeButtons()
+{
+  int i;
+
+  for (i = 0; i < NUM_TAPE_BUTTONS; i++)
+    RedrawGadget(tape_gadget[i]);
+
+  if (tape.show_game_buttons)
+    RedrawGameButtons();
+
+  // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
+  redraw_mask &= ~REDRAW_ALL;
+}
+
+void RedrawOrRemapTapeButtons()
+{
+  if (tape_gadget[TAPE_CTRL_ID_PLAY]->mapped)
+  {
+    // tape buttons already mapped
+    RedrawTapeButtons();
+  }
+  else
+  {
+    UnmapTapeButtons();
+    MapTapeButtons();
+  }
+}
+
 static void HandleTapeButtonsExt(int id)
 {
   if (game_status != GAME_MODE_MAIN && game_status != GAME_MODE_PLAYING)
diff --git a/src/tape.h b/src/tape.h
index cf6f4df..bc80f44 100644
--- a/src/tape.h
+++ b/src/tape.h
@@ -232,6 +232,8 @@ void MapTapeEjectButton();
 void MapTapeWarpButton();
 void MapTapeButtons();
 void UnmapTapeButtons();
+void RedrawTapeButtons();
+void RedrawOrRemapTapeButtons();
 
 void HandleTapeButtonKeys(Key);
 
diff --git a/src/tools.c b/src/tools.c
index b07e19d..6c990cb 100644
--- a/src/tools.c
+++ b/src/tools.c
@@ -759,6 +759,13 @@ void BackToFront()
       y2 = MAX(y2, EY + EYSIZE);
     }
 
+    // make sure that at least one pixel is blitted, and inside the screen
+    // (else nothing is blitted, causing the animations not to be updated)
+    x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
+    y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
+    x2 = MIN(MAX(1, x2), WIN_XSIZE);
+    y2 = MIN(MAX(1, y2), WIN_YSIZE);
+
     BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
   }
 
@@ -3729,6 +3736,9 @@ void WaitForEventToContinue()
 {
   boolean still_wait = TRUE;
 
+  if (program.headless)
+    return;
+
   /* simulate releasing mouse button over last gadget, if still pressed */
   if (button_status)
     HandleGadgets(-1, -1, 0);

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/collab-maint/rocksndiamonds.git



More information about the Pkg-games-commits mailing list