Release Version 2.0.2 [XX XXX XXXX]
-----------------------------------
- added support for TrueColor PCX graphics files
+ - changed default slipping behaviour of gems back to 2.0.0 style;
+ this is now an element property for gems in the level editor,
+ although existing converted levels use the new EM gems behaviour
Release Version 2.0.1 [19 MAR 2002]
-----------------------------------
+ explosion chain reactions are now a bit slower than murphy
- behaviour of robots adjusted to make them less aggressive
(needed for quite some Emerald Mine Club levels)
+ - emeralds and diamonds now fall off normal, steel and growing walls,
+ as this is the correct behaviour in Emerald Mine; existing private
+ and contributed levels will still behave as before, unless saved
+ again (with or without modifications) from the level editor of the
+ current version of the game
- icon for Windows executable added
- bug when selecting default level series fixed
- new IFF style file format for level and tape files
#define GADGET_ID_DOUBLE_SPEED 73
#define GADGET_ID_GRAVITY 74
#define GADGET_ID_STICK_ELEMENT 75
+#define GADGET_ID_EM_SLIPPERY_GEMS 76
/* another drawing area for random placement */
-#define GADGET_ID_RANDOM_BACKGROUND 76
+#define GADGET_ID_RANDOM_BACKGROUND 77
/* gadgets for buttons in element list */
-#define GADGET_ID_ELEMENTLIST_FIRST 77
-#define GADGET_ID_ELEMENTLIST_LAST (77 + ED_NUM_ELEMENTLIST_BUTTONS - 1)
+#define GADGET_ID_ELEMENTLIST_FIRST 78
+#define GADGET_ID_ELEMENTLIST_LAST (78 + ED_NUM_ELEMENTLIST_BUTTONS - 1)
#define NUM_EDITOR_GADGETS (GADGET_ID_ELEMENTLIST_LAST + 1)
#define ED_CHECKBUTTON_ID_GRAVITY 1
#define ED_CHECKBUTTON_ID_RANDOM_RESTRICTED 2
#define ED_CHECKBUTTON_ID_STICK_ELEMENT 3
+#define ED_CHECKBUTTON_ID_EM_SLIPPERY_GEMS 4
-#define ED_NUM_CHECKBUTTONS 4
+#define ED_NUM_CHECKBUTTONS 5
#define ED_CHECKBUTTON_ID_LEVEL_FIRST ED_CHECKBUTTON_ID_DOUBLE_SPEED
#define ED_CHECKBUTTON_ID_LEVEL_LAST ED_CHECKBUTTON_ID_RANDOM_RESTRICTED
GADGET_ID_STICK_ELEMENT,
&stick_element_properties_window,
"stick window to edit content", "stick window to edit content"
+ },
+ {
+ ED_SETTINGS_XPOS, ED_COUNTER_YPOS(4),
+ GADGET_ID_EM_SLIPPERY_GEMS,
+ &level.em_slippery_gems,
+ "slip down from certain flat walls","use EM style slipping behaviour"
}
};
else
DrawElementContentAreas();
}
+
+ if (IS_GEM(properties_element))
+ {
+ /* draw checkbutton gadget */
+ i = ED_CHECKBUTTON_ID_EM_SLIPPERY_GEMS;
+ x = checkbutton_info[i].x + xoffset_right2;
+ y = checkbutton_info[i].y + yoffset_right2;
+
+ DrawTextF(x, y, font_color, checkbutton_info[i].text);
+ ModifyGadget(level_editor_gadget[checkbutton_info[i].gadget_id],
+ GDI_CHECKED, *checkbutton_info[i].value, GDI_END);
+ MapCheckbuttonGadget(i);
+ }
}
static void DrawLineElement(int sx, int sy, int element, boolean change_level)
#define CHUNK_SIZE_NONE -1 /* do not write chunk size */
#define FILE_VERS_CHUNK_SIZE 8 /* size of file version chunk */
#define LEVEL_HEADER_SIZE 80 /* size of level file header */
-#define LEVEL_HEADER_UNUSED 15 /* unused level header bytes */
+#define LEVEL_HEADER_UNUSED 14 /* unused level header bytes */
#define LEVEL_CHUNK_CNT2_SIZE 160 /* size of level CNT2 chunk */
#define LEVEL_CHUNK_CNT2_UNUSED 11 /* unused CNT2 chunk bytes */
#define TAPE_HEADER_SIZE 20 /* size of tape file header */
level.amoeba_content = EL_DIAMANT;
level.double_speed = FALSE;
level.gravity = FALSE;
+ level.em_slippery_gems = FALSE;
for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
level.name[i] = '\0';
level->amoeba_content = checkLevelElement(fgetc(file));
level->double_speed = (fgetc(file) == 1 ? TRUE : FALSE);
level->gravity = (fgetc(file) == 1 ? TRUE : FALSE);
-
level->encoding_16bit_field = (fgetc(file) == 1 ? TRUE : FALSE);
+ level->em_slippery_gems = (fgetc(file) == 1 ? TRUE : FALSE);
ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
fclose(file);
return;
}
+
+ /* pre-2.0 level files have no game version, so use file version here */
+ level.game_version = level.file_version;
}
if (level.file_version < FILE_VERSION_1_2)
if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
IS_LEVELCLASS_USER(leveldir_current))
{
- /* for user contributed and private levels, use the version of
- the game engine the levels were created for */
- level.game_version = level.file_version;
-
- /* player was faster than monsters in pre-1.0 levels */
+ /* For user contributed and private levels, use the version of
+ the game engine the levels were created for.
+ Since 2.0.1, the game engine version is now directly stored
+ in the level file (chunk "VERS"), so there is no need anymore
+ to set the game version from the file version (except for old,
+ pre-2.0 levels, where the game version is still taken from the
+ file format version used to store the level -- see above). */
+
+ /* do some special adjustments to support older level versions */
if (level.file_version == FILE_VERSION_1_0)
{
Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
Error(ERR_WARN, "using high speed movement for player");
+
+ /* player was faster than monsters in (pre-)1.0 levels */
level.double_speed = TRUE;
}
}
else
{
- /* always use the latest version of the game engine for all but
- user contributed and private levels */
+ /* Always use the latest version of the game engine for all but
+ user contributed and private levels; this allows for actual
+ corrections in the game engine to take effect for existing,
+ converted levels (from "classic" or other existing games) to
+ make the game emulation more accurate, while (hopefully) not
+ breaking existing levels created from other players. */
+
level.game_version = GAME_VERSION_ACTUAL;
+
+ /* Set special EM style gems behaviour: EM style gems slip down from
+ normal, steel and growing wall. As this is a more fundamental change,
+ it seems better to set the default behaviour to "off" (as it is more
+ natural) and make it configurable in the level editor (as a property
+ of gem style elements). Already existing converted levels (neither
+ private nor contributed levels) are changed to the new behaviour. */
+
+ if (level.file_version < FILE_VERSION_2_0)
+ level.em_slippery_gems = TRUE;
}
/* determine border element for this level */
file);
fputc((level->double_speed ? 1 : 0), file);
fputc((level->gravity ? 1 : 0), file);
-
fputc((level->encoding_16bit_field ? 1 : 0), file);
+ fputc((level->em_slippery_gems ? 1 : 0), file);
WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
}
fclose(file);
return;
}
- }
- tape.game_version = tape.file_version;
+ /* pre-2.0 tape files have no game version, so use file version here */
+ tape.game_version = tape.file_version;
+ }
if (tape.file_version < FILE_VERSION_1_2)
{
/* dynamically adjust element properties according to game engine version */
{
- static int ep_slippery[] =
+ static int ep_em_slippery_wall[] =
{
EL_BETON,
EL_MAUERWERK,
EL_MAUER_Y,
EL_MAUER_XY
};
- static int ep_slippery_num = sizeof(ep_slippery)/sizeof(int);
+#if 1
+ static int ep_em_slippery_wall_num = SIZEOF_ARRAY_INT(ep_em_slippery_wall);
+#else
+ static int ep_em_slippery_wall_num =
+ sizeof(ep_em_slippery_wall) / sizeof(int);
+#endif
- for (i=0; i<ep_slippery_num; i++)
+ /*
+ printf("level %d: game.version == %06d\n", level_nr, level.game_version);
+ printf(" file_version == %06d\n", level.file_version);
+ */
+
+ for (i=0; i<ep_em_slippery_wall_num; i++)
{
+#if 1
+ if (level.em_slippery_gems) /* special EM style gems behaviour */
+#else
if (game.version >= GAME_VERSION_2_0)
- Elementeigenschaften2[ep_slippery[i]] |= EP_BIT_SLIPPERY_GEMS;
+#endif
+ Elementeigenschaften2[ep_em_slippery_wall[i]] |=
+ EP_BIT_EM_SLIPPERY_WALL;
else
- Elementeigenschaften2[ep_slippery[i]] &= ~EP_BIT_SLIPPERY_GEMS;
+ Elementeigenschaften2[ep_em_slippery_wall[i]] &=
+ ~EP_BIT_EM_SLIPPERY_WALL;
}
}
#endif
#else
else if ((IS_SLIPPERY(Feld[x][y+1]) ||
- (IS_SLIPPERY_GEMS(Feld[x][y+1]) && IS_GEM(element))) &&
+ (IS_EM_SLIPPERY_WALL(Feld[x][y+1]) && IS_GEM(element))) &&
!IS_FALLING(x, y+1) && !JustStopped[x][y+1] &&
element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
#endif
EL_AMOEBE_VOLL,
EL_AMOEBE_BD
};
- static int ep_amoebalive_num = sizeof(ep_amoebalive)/sizeof(int);
+ static int ep_amoebalive_num = SIZEOF_ARRAY_INT(ep_amoebalive);
static int ep_amoeboid[] =
{
EL_AMOEBE_VOLL,
EL_AMOEBE_BD
};
- static int ep_amoeboid_num = sizeof(ep_amoeboid)/sizeof(int);
+ static int ep_amoeboid_num = SIZEOF_ARRAY_INT(ep_amoeboid);
static int ep_schluessel[] =
{
EL_EM_KEY_3,
EL_EM_KEY_4
};
- static int ep_schluessel_num = sizeof(ep_schluessel)/sizeof(int);
+ static int ep_schluessel_num = SIZEOF_ARRAY_INT(ep_schluessel);
static int ep_pforte[] =
{
EL_TUBE_RIGHT_UP,
EL_TUBE_RIGHT_DOWN
};
- static int ep_pforte_num = sizeof(ep_pforte)/sizeof(int);
+ static int ep_pforte_num = SIZEOF_ARRAY_INT(ep_pforte);
static int ep_solid[] =
{
EL_TUBE_RIGHT_UP,
EL_TUBE_RIGHT_DOWN
};
- static int ep_solid_num = sizeof(ep_solid)/sizeof(int);
+ static int ep_solid_num = SIZEOF_ARRAY_INT(ep_solid);
static int ep_massive[] =
{
EL_TUBE_RIGHT_UP,
EL_TUBE_RIGHT_DOWN
};
- static int ep_massive_num = sizeof(ep_massive)/sizeof(int);
+ static int ep_massive_num = SIZEOF_ARRAY_INT(ep_massive);
static int ep_slippery[] =
{
EL_PEARL,
EL_CRYSTAL
};
- static int ep_slippery_num = sizeof(ep_slippery)/sizeof(int);
+ static int ep_slippery_num = SIZEOF_ARRAY_INT(ep_slippery);
static int ep_enemy[] =
{
EL_SP_SNIKSNAK,
EL_SP_ELECTRON
};
- static int ep_enemy_num = sizeof(ep_enemy)/sizeof(int);
+ static int ep_enemy_num = SIZEOF_ARRAY_INT(ep_enemy);
static int ep_mauer[] =
{
EL_EMC_WALL_7,
EL_EMC_WALL_8
};
- static int ep_mauer_num = sizeof(ep_mauer)/sizeof(int);
+ static int ep_mauer_num = SIZEOF_ARRAY_INT(ep_mauer);
static int ep_can_fall[] =
{
EL_SPRING,
EL_DX_SUPABOMB
};
- static int ep_can_fall_num = sizeof(ep_can_fall)/sizeof(int);
+ static int ep_can_fall_num = SIZEOF_ARRAY_INT(ep_can_fall);
static int ep_can_smash[] =
{
EL_SPRING,
EL_DX_SUPABOMB
};
- static int ep_can_smash_num = sizeof(ep_can_smash)/sizeof(int);
+ static int ep_can_smash_num = SIZEOF_ARRAY_INT(ep_can_smash);
static int ep_can_change[] =
{
EL_EDELSTEIN_LILA,
EL_DIAMANT
};
- static int ep_can_change_num = sizeof(ep_can_change)/sizeof(int);
+ static int ep_can_change_num = SIZEOF_ARRAY_INT(ep_can_change);
static int ep_can_move[] =
{
EL_BALLOON,
EL_SPRING_MOVING
};
- static int ep_can_move_num = sizeof(ep_can_move)/sizeof(int);
+ static int ep_can_move_num = SIZEOF_ARRAY_INT(ep_can_move);
static int ep_could_move[] =
{
EL_PACMAN_LEFT,
EL_PACMAN_DOWN
};
- static int ep_could_move_num = sizeof(ep_could_move)/sizeof(int);
+ static int ep_could_move_num = SIZEOF_ARRAY_INT(ep_could_move);
static int ep_dont_touch[] =
{
EL_BUTTERFLY,
EL_FIREFLY
};
- static int ep_dont_touch_num = sizeof(ep_dont_touch)/sizeof(int);
+ static int ep_dont_touch_num = SIZEOF_ARRAY_INT(ep_dont_touch);
static int ep_dont_go_to[] =
{
EL_TRAP_ACTIVE,
EL_LANDMINE
};
- static int ep_dont_go_to_num = sizeof(ep_dont_go_to)/sizeof(int);
+ static int ep_dont_go_to_num = SIZEOF_ARRAY_INT(ep_dont_go_to);
static int ep_mampf2[] =
{
EL_PEARL,
EL_CRYSTAL
};
- static int ep_mampf2_num = sizeof(ep_mampf2)/sizeof(int);
+ static int ep_mampf2_num = SIZEOF_ARRAY_INT(ep_mampf2);
static int ep_bd_element[] =
{
EL_AMOEBE_BD,
EL_CHAR_FRAGE
};
- static int ep_bd_element_num = sizeof(ep_bd_element)/sizeof(int);
+ static int ep_bd_element_num = SIZEOF_ARRAY_INT(ep_bd_element);
static int ep_sb_element[] =
{
EL_SPIELFIGUR,
EL_INVISIBLE_STEEL
};
- static int ep_sb_element_num = sizeof(ep_sb_element)/sizeof(int);
+ static int ep_sb_element_num = SIZEOF_ARRAY_INT(ep_sb_element);
static int ep_gem[] =
{
EL_EDELSTEIN_LILA,
EL_DIAMANT
};
- static int ep_gem_num = sizeof(ep_gem)/sizeof(int);
+ static int ep_gem_num = SIZEOF_ARRAY_INT(ep_gem);
static int ep_inactive[] =
{
EL_EMC_WALL_7,
EL_EMC_WALL_8
};
- static int ep_inactive_num = sizeof(ep_inactive)/sizeof(int);
+ static int ep_inactive_num = SIZEOF_ARRAY_INT(ep_inactive);
static int ep_explosive[] =
{
EL_SP_ELECTRON,
EL_DX_SUPABOMB
};
- static int ep_explosive_num = sizeof(ep_explosive)/sizeof(int);
+ static int ep_explosive_num = SIZEOF_ARRAY_INT(ep_explosive);
static int ep_mampf3[] =
{
EL_PEARL,
EL_CRYSTAL
};
- static int ep_mampf3_num = sizeof(ep_mampf3)/sizeof(int);
+ static int ep_mampf3_num = SIZEOF_ARRAY_INT(ep_mampf3);
static int ep_pushable[] =
{
EL_SPRING,
EL_DX_SUPABOMB
};
- static int ep_pushable_num = sizeof(ep_pushable)/sizeof(int);
+ static int ep_pushable_num = SIZEOF_ARRAY_INT(ep_pushable);
static int ep_player[] =
{
EL_SPIELER3,
EL_SPIELER4
};
- static int ep_player_num = sizeof(ep_player)/sizeof(int);
+ static int ep_player_num = SIZEOF_ARRAY_INT(ep_player);
static int ep_has_content[] =
{
EL_AMOEBE_VOLL,
EL_AMOEBE_BD
};
- static int ep_has_content_num = sizeof(ep_has_content)/sizeof(int);
+ static int ep_has_content_num = SIZEOF_ARRAY_INT(ep_has_content);
static int ep_eatable[] =
{
EL_TRAP_INACTIVE,
EL_SAND_INVISIBLE
};
- static int ep_eatable_num = sizeof(ep_eatable)/sizeof(int);
+ static int ep_eatable_num = SIZEOF_ARRAY_INT(ep_eatable);
static int ep_sp_element[] =
{
/* more than one murphy in a level results in an inactive clone */
EL_SP_MURPHY_CLONE
};
- static int ep_sp_element_num = sizeof(ep_sp_element)/sizeof(int);
+ static int ep_sp_element_num = SIZEOF_ARRAY_INT(ep_sp_element);
static int ep_quick_gate[] =
{
EL_SWITCHGATE_OPEN,
EL_TIMEGATE_OPEN
};
- static int ep_quick_gate_num = sizeof(ep_quick_gate)/sizeof(int);
+ static int ep_quick_gate_num = SIZEOF_ARRAY_INT(ep_quick_gate);
static int ep_over_player[] =
{
EL_TUBE_RIGHT_UP,
EL_TUBE_RIGHT_DOWN
};
- static int ep_over_player_num = sizeof(ep_over_player)/sizeof(int);
+ static int ep_over_player_num = SIZEOF_ARRAY_INT(ep_over_player);
static int ep_active_bomb[] =
{
EL_DYNABOMB_ACTIVE_3,
EL_DYNABOMB_ACTIVE_4
};
- static int ep_active_bomb_num = sizeof(ep_active_bomb)/sizeof(int);
+ static int ep_active_bomb_num = SIZEOF_ARRAY_INT(ep_active_bomb);
static int ep_belt[] =
{
EL_BELT4_MIDDLE,
EL_BELT4_RIGHT,
};
- static int ep_belt_num = sizeof(ep_belt)/sizeof(int);
+ static int ep_belt_num = SIZEOF_ARRAY_INT(ep_belt);
static int ep_belt_switch[] =
{
EL_BELT4_SWITCH_MIDDLE,
EL_BELT4_SWITCH_RIGHT,
};
- static int ep_belt_switch_num = sizeof(ep_belt_switch)/sizeof(int);
+ static int ep_belt_switch_num = SIZEOF_ARRAY_INT(ep_belt_switch);
static int ep_tube[] =
{
EL_TUBE_RIGHT_UP,
EL_TUBE_RIGHT_DOWN
};
- static int ep_tube_num = sizeof(ep_tube)/sizeof(int);
+ static int ep_tube_num = SIZEOF_ARRAY_INT(ep_tube);
static long ep1_bit[] =
{
&ep_belt_switch_num,
&ep_tube_num
};
- static int num_properties1 = sizeof(ep1_num)/sizeof(int *);
- static int num_properties2 = sizeof(ep2_num)/sizeof(int *);
+ static int num_properties1 = SIZEOF_ARRAY(ep1_num, int *);
+ static int num_properties2 = SIZEOF_ARRAY(ep2_num, int *);
for(i=0; i<MAX_ELEMENTS; i++)
{
break;
case GDI_CHECKED:
- gi->checked = va_arg(ap, boolean);
+ /* take care here: "boolean" is typedef'ed as "unsigned char",
+ which gets promoted to "int" */
+ gi->checked = (boolean)va_arg(ap, int);
break;
case GDI_RADIO_NR:
#define SIGN(a) ((a) < 0 ? -1 : ((a)>0 ? 1 : 0))
#endif
+#define SIZEOF_ARRAY(array, type) (sizeof(array) / sizeof(type))
+#define SIZEOF_ARRAY_INT(array) SIZEOF_ARRAY(array, int)
+
#endif /* TYPES_H */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <time.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#define EP_BIT_BELT (1 << 0)
#define EP_BIT_BELT_SWITCH (1 << 1)
#define EP_BIT_TUBE (1 << 2)
-#define EP_BIT_SLIPPERY_GEMS (1 << 3)
+#define EP_BIT_EM_SLIPPERY_WALL (1 << 3)
#define IS_AMOEBALIVE(e) (Elementeigenschaften1[e] & EP_BIT_AMOEBALIVE)
#define IS_AMOEBOID(e) (Elementeigenschaften1[e] & EP_BIT_AMOEBOID)
#define IS_BELT(e) (Elementeigenschaften2[e] & EP_BIT_BELT)
#define IS_BELT_SWITCH(e) (Elementeigenschaften2[e] & EP_BIT_BELT_SWITCH)
#define IS_TUBE(e) (Elementeigenschaften2[e] & EP_BIT_TUBE)
-#define IS_SLIPPERY_GEMS(e) (Elementeigenschaften2[e] & EP_BIT_SLIPPERY_GEMS)
+#define IS_EM_SLIPPERY_WALL(e) (Elementeigenschaften2[e] & EP_BIT_EM_SLIPPERY_WALL)
#define IS_PLAYER(x,y) (ELEM_IS_PLAYER(StorePlayer[x][y]))
struct LevelInfo
{
- int file_version; /* version of file the level was stored with */
- int game_version; /* version of game engine to play this level */
- boolean encoding_16bit_field; /* level contains 16-bit elements */
+ int file_version; /* file format version the level is stored with */
+ int game_version; /* game engine version the level was created with */
+ boolean encoding_16bit_field; /* level contains 16-bit elements */
boolean encoding_16bit_yamyam; /* yamyam contains 16-bit elements */
boolean encoding_16bit_amoeba; /* amoeba contains 16-bit elements */
int time_timegate;
boolean double_speed;
boolean gravity;
+ boolean em_slippery_gems; /* EM style "gems slip from wall" behaviour */
};
struct TapeInfo
{
- int file_version; /* version of file this level tape was stored with */
- int game_version; /* version of game engine to play this tapeĀ“s level */
+ int file_version; /* file format version the tape is stored with */
+ int game_version; /* game engine version the tape was created with */
int version;
int level_nr;
unsigned long random_seed;
#define PROGRAM_TITLE_STRING "Rocks'n'Diamonds"
#define PROGRAM_AUTHOR_STRING "Holger Schemel"
-#define PROGRAM_RIGHTS_STRING "Copyright ^1995-2001 by"
+#define PROGRAM_RIGHTS_STRING "Copyright ^1995-2002 by"
#define PROGRAM_DOS_PORT_STRING "DOS port done by Guido Schulz"
#define PROGRAM_IDENT_STRING PROGRAM_VERSION_STRING " " TARGET_STRING
#define WINDOW_TITLE_STRING PROGRAM_TITLE_STRING " " PROGRAM_IDENT_STRING
#ifndef TOOLS_H
#define TOOLS_H
-#include <sys/time.h>
#include "main.h"
/* for SetDrawtoField */