-/***********************************************************
-* Rocks'n'Diamonds -- McDuffin Strikes Back! *
-*----------------------------------------------------------*
-* (c) 1995-2002 Artsoft Entertainment *
-* Holger Schemel *
-* Detmolder Strasse 189 *
-* 33604 Bielefeld *
-* Germany *
-* e-mail: info@artsoft.org *
-*----------------------------------------------------------*
-* game.c *
-***********************************************************/
+// ============================================================================
+// Rocks'n'Diamonds - McDuffin Strikes Back!
+// ----------------------------------------------------------------------------
+// (c) 1995-2014 by Artsoft Entertainment
+// Holger Schemel
+// info@artsoft.org
+// http://www.artsoft.org/
+// ----------------------------------------------------------------------------
+// game.c
+// ============================================================================
#include "libgame/libgame.h"
#include "init.h"
#include "tools.h"
#include "screens.h"
+#include "events.h"
#include "files.h"
#include "tape.h"
#include "network.h"
+
+/* DEBUG SETTINGS */
+#define DEBUG_INIT_PLAYER 1
+#define DEBUG_PLAYER_ACTIONS 0
+
/* EXPERIMENTAL STUFF */
#define USE_NEW_AMOEBA_CODE FALSE
/* EXPERIMENTAL STUFF */
-#define USE_NEW_STUFF ( 1)
-
-#define USE_NEW_SP_SLIPPERY (USE_NEW_STUFF * 1)
-#define USE_NEW_CUSTOM_VALUE (USE_NEW_STUFF * 1)
-#define USE_NEW_PLAYER_ANIM (USE_NEW_STUFF * 1)
-#define USE_NEW_ALL_SLIPPERY (USE_NEW_STUFF * 1)
-#define USE_NEW_PLAYER_SPEED (USE_NEW_STUFF * 1)
-#define USE_NEW_DELAYED_ACTION (USE_NEW_STUFF * 1)
-#define USE_NEW_SNAP_DELAY (USE_NEW_STUFF * 1)
-#define USE_ONLY_ONE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
-#define USE_ONE_MORE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
-#define USE_FIXED_DONT_RUN_INTO (USE_NEW_STUFF * 1)
-#define USE_NEW_SPRING_BUMPER (USE_NEW_STUFF * 1)
-#define USE_STOP_CHANGED_ELEMENTS (USE_NEW_STUFF * 1)
-
-#define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0)
+#define USE_QUICKSAND_BD_ROCK_BUGFIX 0
+#define USE_QUICKSAND_IMPACT_BUGFIX 0
+#define USE_DELAYED_GFX_REDRAW 0
+#define USE_NEW_PLAYER_ASSIGNMENTS 1
+
+#if USE_DELAYED_GFX_REDRAW
+#define TEST_DrawLevelField(x, y) \
+ GfxRedraw[x][y] |= GFX_REDRAW_TILE
+#define TEST_DrawLevelFieldCrumbled(x, y) \
+ GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
+#define TEST_DrawLevelFieldCrumbledNeighbours(x, y) \
+ GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
+#define TEST_DrawTwinkleOnField(x, y) \
+ GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
+#else
+#define TEST_DrawLevelField(x, y) \
+ DrawLevelField(x, y)
+#define TEST_DrawLevelFieldCrumbled(x, y) \
+ DrawLevelFieldCrumbled(x, y)
+#define TEST_DrawLevelFieldCrumbledNeighbours(x, y) \
+ DrawLevelFieldCrumbledNeighbours(x, y)
+#define TEST_DrawTwinkleOnField(x, y) \
+ DrawTwinkleOnField(x, y)
+#endif
+
/* for DigField() */
#define DF_NO_PUSH 0
#define EX_TYPE_DYNA (1 << 4)
#define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
-/* special positions in the game control window (relative to control window) */
-#define XX_LEVEL 37
-#define YY_LEVEL 20
-#define XX_EMERALDS 29
-#define YY_EMERALDS 54
-#define XX_DYNAMITE 29
-#define YY_DYNAMITE 89
-#define XX_KEYS 18
-#define YY_KEYS 123
-#define XX_SCORE 15
-#define YY_SCORE 159
-#define XX_TIME1 29
-#define XX_TIME2 30
-#define YY_TIME 194
-
-/* special positions in the game control window (relative to main window) */
-#define DX_LEVEL (DX + XX_LEVEL)
-#define DY_LEVEL (DY + YY_LEVEL)
-#define DX_EMERALDS (DX + XX_EMERALDS)
-#define DY_EMERALDS (DY + YY_EMERALDS)
-#define DX_DYNAMITE (DX + XX_DYNAMITE)
-#define DY_DYNAMITE (DY + YY_DYNAMITE)
-#define DX_KEYS (DX + XX_KEYS)
-#define DY_KEYS (DY + YY_KEYS)
-#define DX_SCORE (DX + XX_SCORE)
-#define DY_SCORE (DY + YY_SCORE)
-#define DX_TIME1 (DX + XX_TIME1)
-#define DX_TIME2 (DX + XX_TIME2)
-#define DY_TIME (DY + YY_TIME)
+#define PANEL_OFF() (local_player->LevelSolved_PanelOff)
+#define PANEL_DEACTIVATED(p) ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
+#define PANEL_XPOS(p) (DX + ALIGNED_TEXT_XPOS(p))
+#define PANEL_YPOS(p) (DY + ALIGNED_TEXT_YPOS(p))
+
+/* game panel display and control definitions */
+#define GAME_PANEL_LEVEL_NUMBER 0
+#define GAME_PANEL_GEMS 1
+#define GAME_PANEL_INVENTORY_COUNT 2
+#define GAME_PANEL_INVENTORY_FIRST_1 3
+#define GAME_PANEL_INVENTORY_FIRST_2 4
+#define GAME_PANEL_INVENTORY_FIRST_3 5
+#define GAME_PANEL_INVENTORY_FIRST_4 6
+#define GAME_PANEL_INVENTORY_FIRST_5 7
+#define GAME_PANEL_INVENTORY_FIRST_6 8
+#define GAME_PANEL_INVENTORY_FIRST_7 9
+#define GAME_PANEL_INVENTORY_FIRST_8 10
+#define GAME_PANEL_INVENTORY_LAST_1 11
+#define GAME_PANEL_INVENTORY_LAST_2 12
+#define GAME_PANEL_INVENTORY_LAST_3 13
+#define GAME_PANEL_INVENTORY_LAST_4 14
+#define GAME_PANEL_INVENTORY_LAST_5 15
+#define GAME_PANEL_INVENTORY_LAST_6 16
+#define GAME_PANEL_INVENTORY_LAST_7 17
+#define GAME_PANEL_INVENTORY_LAST_8 18
+#define GAME_PANEL_KEY_1 19
+#define GAME_PANEL_KEY_2 20
+#define GAME_PANEL_KEY_3 21
+#define GAME_PANEL_KEY_4 22
+#define GAME_PANEL_KEY_5 23
+#define GAME_PANEL_KEY_6 24
+#define GAME_PANEL_KEY_7 25
+#define GAME_PANEL_KEY_8 26
+#define GAME_PANEL_KEY_WHITE 27
+#define GAME_PANEL_KEY_WHITE_COUNT 28
+#define GAME_PANEL_SCORE 29
+#define GAME_PANEL_HIGHSCORE 30
+#define GAME_PANEL_TIME 31
+#define GAME_PANEL_TIME_HH 32
+#define GAME_PANEL_TIME_MM 33
+#define GAME_PANEL_TIME_SS 34
+#define GAME_PANEL_FRAME 35
+#define GAME_PANEL_SHIELD_NORMAL 36
+#define GAME_PANEL_SHIELD_NORMAL_TIME 37
+#define GAME_PANEL_SHIELD_DEADLY 38
+#define GAME_PANEL_SHIELD_DEADLY_TIME 39
+#define GAME_PANEL_EXIT 40
+#define GAME_PANEL_EMC_MAGIC_BALL 41
+#define GAME_PANEL_EMC_MAGIC_BALL_SWITCH 42
+#define GAME_PANEL_LIGHT_SWITCH 43
+#define GAME_PANEL_LIGHT_SWITCH_TIME 44
+#define GAME_PANEL_TIMEGATE_SWITCH 45
+#define GAME_PANEL_TIMEGATE_SWITCH_TIME 46
+#define GAME_PANEL_SWITCHGATE_SWITCH 47
+#define GAME_PANEL_EMC_LENSES 48
+#define GAME_PANEL_EMC_LENSES_TIME 49
+#define GAME_PANEL_EMC_MAGNIFIER 50
+#define GAME_PANEL_EMC_MAGNIFIER_TIME 51
+#define GAME_PANEL_BALLOON_SWITCH 52
+#define GAME_PANEL_DYNABOMB_NUMBER 53
+#define GAME_PANEL_DYNABOMB_SIZE 54
+#define GAME_PANEL_DYNABOMB_POWER 55
+#define GAME_PANEL_PENGUINS 56
+#define GAME_PANEL_SOKOBAN_OBJECTS 57
+#define GAME_PANEL_SOKOBAN_FIELDS 58
+#define GAME_PANEL_ROBOT_WHEEL 59
+#define GAME_PANEL_CONVEYOR_BELT_1 60
+#define GAME_PANEL_CONVEYOR_BELT_2 61
+#define GAME_PANEL_CONVEYOR_BELT_3 62
+#define GAME_PANEL_CONVEYOR_BELT_4 63
+#define GAME_PANEL_CONVEYOR_BELT_1_SWITCH 64
+#define GAME_PANEL_CONVEYOR_BELT_2_SWITCH 65
+#define GAME_PANEL_CONVEYOR_BELT_3_SWITCH 66
+#define GAME_PANEL_CONVEYOR_BELT_4_SWITCH 67
+#define GAME_PANEL_MAGIC_WALL 68
+#define GAME_PANEL_MAGIC_WALL_TIME 69
+#define GAME_PANEL_GRAVITY_STATE 70
+#define GAME_PANEL_GRAPHIC_1 71
+#define GAME_PANEL_GRAPHIC_2 72
+#define GAME_PANEL_GRAPHIC_3 73
+#define GAME_PANEL_GRAPHIC_4 74
+#define GAME_PANEL_GRAPHIC_5 75
+#define GAME_PANEL_GRAPHIC_6 76
+#define GAME_PANEL_GRAPHIC_7 77
+#define GAME_PANEL_GRAPHIC_8 78
+#define GAME_PANEL_ELEMENT_1 79
+#define GAME_PANEL_ELEMENT_2 80
+#define GAME_PANEL_ELEMENT_3 81
+#define GAME_PANEL_ELEMENT_4 82
+#define GAME_PANEL_ELEMENT_5 83
+#define GAME_PANEL_ELEMENT_6 84
+#define GAME_PANEL_ELEMENT_7 85
+#define GAME_PANEL_ELEMENT_8 86
+#define GAME_PANEL_ELEMENT_COUNT_1 87
+#define GAME_PANEL_ELEMENT_COUNT_2 88
+#define GAME_PANEL_ELEMENT_COUNT_3 89
+#define GAME_PANEL_ELEMENT_COUNT_4 90
+#define GAME_PANEL_ELEMENT_COUNT_5 91
+#define GAME_PANEL_ELEMENT_COUNT_6 92
+#define GAME_PANEL_ELEMENT_COUNT_7 93
+#define GAME_PANEL_ELEMENT_COUNT_8 94
+#define GAME_PANEL_CE_SCORE_1 95
+#define GAME_PANEL_CE_SCORE_2 96
+#define GAME_PANEL_CE_SCORE_3 97
+#define GAME_PANEL_CE_SCORE_4 98
+#define GAME_PANEL_CE_SCORE_5 99
+#define GAME_PANEL_CE_SCORE_6 100
+#define GAME_PANEL_CE_SCORE_7 101
+#define GAME_PANEL_CE_SCORE_8 102
+#define GAME_PANEL_CE_SCORE_1_ELEMENT 103
+#define GAME_PANEL_CE_SCORE_2_ELEMENT 104
+#define GAME_PANEL_CE_SCORE_3_ELEMENT 105
+#define GAME_PANEL_CE_SCORE_4_ELEMENT 106
+#define GAME_PANEL_CE_SCORE_5_ELEMENT 107
+#define GAME_PANEL_CE_SCORE_6_ELEMENT 108
+#define GAME_PANEL_CE_SCORE_7_ELEMENT 109
+#define GAME_PANEL_CE_SCORE_8_ELEMENT 110
+#define GAME_PANEL_PLAYER_NAME 111
+#define GAME_PANEL_LEVEL_NAME 112
+#define GAME_PANEL_LEVEL_AUTHOR 113
+
+#define NUM_GAME_PANEL_CONTROLS 114
+
+struct GamePanelOrderInfo
+{
+ int nr;
+ int sort_priority;
+};
+
+static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
+
+struct GamePanelControlInfo
+{
+ int nr;
+
+ struct TextPosInfo *pos;
+ int type;
+
+ int value, last_value;
+ int frame, last_frame;
+ int gfx_frame;
+ int gfx_random;
+};
+
+static struct GamePanelControlInfo game_panel_controls[] =
+{
+ {
+ GAME_PANEL_LEVEL_NUMBER,
+ &game.panel.level_number,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_GEMS,
+ &game.panel.gems,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_INVENTORY_COUNT,
+ &game.panel.inventory_count,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_INVENTORY_FIRST_1,
+ &game.panel.inventory_first[0],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_INVENTORY_FIRST_2,
+ &game.panel.inventory_first[1],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_INVENTORY_FIRST_3,
+ &game.panel.inventory_first[2],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_INVENTORY_FIRST_4,
+ &game.panel.inventory_first[3],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_INVENTORY_FIRST_5,
+ &game.panel.inventory_first[4],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_INVENTORY_FIRST_6,
+ &game.panel.inventory_first[5],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_INVENTORY_FIRST_7,
+ &game.panel.inventory_first[6],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_INVENTORY_FIRST_8,
+ &game.panel.inventory_first[7],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_INVENTORY_LAST_1,
+ &game.panel.inventory_last[0],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_INVENTORY_LAST_2,
+ &game.panel.inventory_last[1],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_INVENTORY_LAST_3,
+ &game.panel.inventory_last[2],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_INVENTORY_LAST_4,
+ &game.panel.inventory_last[3],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_INVENTORY_LAST_5,
+ &game.panel.inventory_last[4],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_INVENTORY_LAST_6,
+ &game.panel.inventory_last[5],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_INVENTORY_LAST_7,
+ &game.panel.inventory_last[6],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_INVENTORY_LAST_8,
+ &game.panel.inventory_last[7],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_KEY_1,
+ &game.panel.key[0],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_KEY_2,
+ &game.panel.key[1],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_KEY_3,
+ &game.panel.key[2],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_KEY_4,
+ &game.panel.key[3],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_KEY_5,
+ &game.panel.key[4],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_KEY_6,
+ &game.panel.key[5],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_KEY_7,
+ &game.panel.key[6],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_KEY_8,
+ &game.panel.key[7],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_KEY_WHITE,
+ &game.panel.key_white,
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_KEY_WHITE_COUNT,
+ &game.panel.key_white_count,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_SCORE,
+ &game.panel.score,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_HIGHSCORE,
+ &game.panel.highscore,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_TIME,
+ &game.panel.time,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_TIME_HH,
+ &game.panel.time_hh,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_TIME_MM,
+ &game.panel.time_mm,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_TIME_SS,
+ &game.panel.time_ss,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_FRAME,
+ &game.panel.frame,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_SHIELD_NORMAL,
+ &game.panel.shield_normal,
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_SHIELD_NORMAL_TIME,
+ &game.panel.shield_normal_time,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_SHIELD_DEADLY,
+ &game.panel.shield_deadly,
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_SHIELD_DEADLY_TIME,
+ &game.panel.shield_deadly_time,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_EXIT,
+ &game.panel.exit,
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_EMC_MAGIC_BALL,
+ &game.panel.emc_magic_ball,
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
+ &game.panel.emc_magic_ball_switch,
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_LIGHT_SWITCH,
+ &game.panel.light_switch,
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_LIGHT_SWITCH_TIME,
+ &game.panel.light_switch_time,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_TIMEGATE_SWITCH,
+ &game.panel.timegate_switch,
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_TIMEGATE_SWITCH_TIME,
+ &game.panel.timegate_switch_time,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_SWITCHGATE_SWITCH,
+ &game.panel.switchgate_switch,
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_EMC_LENSES,
+ &game.panel.emc_lenses,
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_EMC_LENSES_TIME,
+ &game.panel.emc_lenses_time,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_EMC_MAGNIFIER,
+ &game.panel.emc_magnifier,
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_EMC_MAGNIFIER_TIME,
+ &game.panel.emc_magnifier_time,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_BALLOON_SWITCH,
+ &game.panel.balloon_switch,
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_DYNABOMB_NUMBER,
+ &game.panel.dynabomb_number,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_DYNABOMB_SIZE,
+ &game.panel.dynabomb_size,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_DYNABOMB_POWER,
+ &game.panel.dynabomb_power,
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_PENGUINS,
+ &game.panel.penguins,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_SOKOBAN_OBJECTS,
+ &game.panel.sokoban_objects,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_SOKOBAN_FIELDS,
+ &game.panel.sokoban_fields,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_ROBOT_WHEEL,
+ &game.panel.robot_wheel,
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_CONVEYOR_BELT_1,
+ &game.panel.conveyor_belt[0],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_CONVEYOR_BELT_2,
+ &game.panel.conveyor_belt[1],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_CONVEYOR_BELT_3,
+ &game.panel.conveyor_belt[2],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_CONVEYOR_BELT_4,
+ &game.panel.conveyor_belt[3],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
+ &game.panel.conveyor_belt_switch[0],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
+ &game.panel.conveyor_belt_switch[1],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
+ &game.panel.conveyor_belt_switch[2],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
+ &game.panel.conveyor_belt_switch[3],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_MAGIC_WALL,
+ &game.panel.magic_wall,
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_MAGIC_WALL_TIME,
+ &game.panel.magic_wall_time,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_GRAVITY_STATE,
+ &game.panel.gravity_state,
+ TYPE_STRING,
+ },
+ {
+ GAME_PANEL_GRAPHIC_1,
+ &game.panel.graphic[0],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_GRAPHIC_2,
+ &game.panel.graphic[1],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_GRAPHIC_3,
+ &game.panel.graphic[2],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_GRAPHIC_4,
+ &game.panel.graphic[3],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_GRAPHIC_5,
+ &game.panel.graphic[4],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_GRAPHIC_6,
+ &game.panel.graphic[5],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_GRAPHIC_7,
+ &game.panel.graphic[6],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_GRAPHIC_8,
+ &game.panel.graphic[7],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_ELEMENT_1,
+ &game.panel.element[0],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_ELEMENT_2,
+ &game.panel.element[1],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_ELEMENT_3,
+ &game.panel.element[2],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_ELEMENT_4,
+ &game.panel.element[3],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_ELEMENT_5,
+ &game.panel.element[4],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_ELEMENT_6,
+ &game.panel.element[5],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_ELEMENT_7,
+ &game.panel.element[6],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_ELEMENT_8,
+ &game.panel.element[7],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_ELEMENT_COUNT_1,
+ &game.panel.element_count[0],
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_ELEMENT_COUNT_2,
+ &game.panel.element_count[1],
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_ELEMENT_COUNT_3,
+ &game.panel.element_count[2],
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_ELEMENT_COUNT_4,
+ &game.panel.element_count[3],
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_ELEMENT_COUNT_5,
+ &game.panel.element_count[4],
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_ELEMENT_COUNT_6,
+ &game.panel.element_count[5],
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_ELEMENT_COUNT_7,
+ &game.panel.element_count[6],
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_ELEMENT_COUNT_8,
+ &game.panel.element_count[7],
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_CE_SCORE_1,
+ &game.panel.ce_score[0],
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_CE_SCORE_2,
+ &game.panel.ce_score[1],
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_CE_SCORE_3,
+ &game.panel.ce_score[2],
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_CE_SCORE_4,
+ &game.panel.ce_score[3],
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_CE_SCORE_5,
+ &game.panel.ce_score[4],
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_CE_SCORE_6,
+ &game.panel.ce_score[5],
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_CE_SCORE_7,
+ &game.panel.ce_score[6],
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_CE_SCORE_8,
+ &game.panel.ce_score[7],
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_CE_SCORE_1_ELEMENT,
+ &game.panel.ce_score_element[0],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_CE_SCORE_2_ELEMENT,
+ &game.panel.ce_score_element[1],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_CE_SCORE_3_ELEMENT,
+ &game.panel.ce_score_element[2],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_CE_SCORE_4_ELEMENT,
+ &game.panel.ce_score_element[3],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_CE_SCORE_5_ELEMENT,
+ &game.panel.ce_score_element[4],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_CE_SCORE_6_ELEMENT,
+ &game.panel.ce_score_element[5],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_CE_SCORE_7_ELEMENT,
+ &game.panel.ce_score_element[6],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_CE_SCORE_8_ELEMENT,
+ &game.panel.ce_score_element[7],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_PLAYER_NAME,
+ &game.panel.player_name,
+ TYPE_STRING,
+ },
+ {
+ GAME_PANEL_LEVEL_NAME,
+ &game.panel.level_name,
+ TYPE_STRING,
+ },
+ {
+ GAME_PANEL_LEVEL_AUTHOR,
+ &game.panel.level_author,
+ TYPE_STRING,
+ },
+
+ {
+ -1,
+ NULL,
+ -1,
+ }
+};
+
+/* values for delayed check of falling and moving elements and for collision */
+#define CHECK_DELAY_MOVING 3
+#define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
+#define CHECK_DELAY_COLLISION 2
+#define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
/* values for initial player move delay (initial delay counter value) */
#define INITIAL_MOVE_DELAY_OFF -1
#define MOVE_DELAY_HIGH_SPEED 4
#define MOVE_DELAY_MAX_SPEED 1
-#if 0
-#define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
-#define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
-#else
#define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
#define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
-#endif
-#define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
+
+#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 other actions */
#define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
#define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
-#define INIT_GFX_RANDOM() (SimpleRND(1000000))
+#define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
#define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
RND(element_info[e].push_delay_random))
RND(element_info[e].move_delay_random))
#define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
(element_info[e].move_delay_random))
-#define GET_NEW_CUSTOM_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
+#define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
RND(element_info[e].ce_value_random_initial))
+#define GET_CE_SCORE(e) ( (element_info[e].collect_score))
#define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
RND((c)->delay_random * (c)->delay_frames))
#define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
RND((c)->delay_random))
-#if 1
+
#define GET_VALID_RUNTIME_ELEMENT(e) \
((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
-#else
-#define GET_VALID_FILE_ELEMENT(e) \
- ((e) >= NUM_FILE_ELEMENTS ? EL_UNKNOWN : (e))
-#endif
-#define GET_TARGET_ELEMENT(e, ch) \
- ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
- (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
- (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : (e))
+#define RESOLVED_REFERENCE_ELEMENT(be, e) \
+ ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
+ (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
+ (be) + (e) - EL_SELF)
+
+#define GET_PLAYER_FROM_BITS(p) \
+ (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
+
+#define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
+ ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
+ (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
+ (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
+ (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
+ (e) == EL_CURRENT_CE_VALUE ? (cv) : \
+ (e) == EL_CURRENT_CE_SCORE ? (cs) : \
+ (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
+ RESOLVED_REFERENCE_ELEMENT(be, e) : \
+ (e))
#define CAN_GROW_INTO(e) \
((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
#define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
- ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
+ ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
+ Feld[x][y] == EL_EM_EXIT_OPEN || \
+ Feld[x][y] == EL_STEEL_EXIT_OPEN || \
+ Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
IS_FOOD_PENGUIN(Feld[x][y])))
#define DRAGON_CAN_ENTER_FIELD(e, x, y) \
ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
(IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
-#if 0
-#define GROUP_NR(e) ((e) - EL_GROUP_START)
-#define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
-#define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
-
-#define IS_EQUAL_OR_IN_GROUP(e, ge) \
- (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
-#endif
-
#define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
#define CE_ENTER_FIELD_COND(e, x, y) \
#define GAME_CTRL_ID_STOP 0
#define GAME_CTRL_ID_PAUSE 1
#define GAME_CTRL_ID_PLAY 2
-#define SOUND_CTRL_ID_MUSIC 3
-#define SOUND_CTRL_ID_LOOPS 4
-#define SOUND_CTRL_ID_SIMPLE 5
+#define GAME_CTRL_ID_UNDO 3
+#define GAME_CTRL_ID_REDO 4
+#define GAME_CTRL_ID_SAVE 5
+#define GAME_CTRL_ID_PAUSE2 6
+#define GAME_CTRL_ID_LOAD 7
+#define SOUND_CTRL_ID_MUSIC 8
+#define SOUND_CTRL_ID_LOOPS 9
+#define SOUND_CTRL_ID_SIMPLE 10
-#define NUM_GAME_BUTTONS 6
+#define NUM_GAME_BUTTONS 11
/* forward declaration for internal use */
static void CreateField(int, int, int);
+static void ResetGfxAnimation(int, int);
+
static void SetPlayerWaiting(struct PlayerInfo *, boolean);
static void AdvanceFrameAndPlayerCounters(int);
static void ScrollPlayer(struct PlayerInfo *, int);
static void ScrollScreen(struct PlayerInfo *, int);
-int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
+static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
+static boolean DigFieldByCE(int, int, int);
+static boolean SnapField(struct PlayerInfo *, int, int);
+static boolean DropElement(struct PlayerInfo *);
static void InitBeltMovement(void);
static void CloseAllOpenTimegates(void);
static void TestIfPlayerTouchesCustomElement(int, int);
static void TestIfElementTouchesCustomElement(int, int);
static void TestIfElementHitsCustomElement(int, int, int);
-#if 0
-static void TestIfElementSmashesCustomElement(int, int, int);
-#endif
static void HandleElementChange(int, int, int);
static void ExecuteCustomElementAction(int, int, int, int);
static void StopLevelSoundActionIfLoop(int, int, int);
static void PlayLevelMusic();
-static void MapGameButtons();
static void HandleGameButtons(struct GadgetInfo *);
+int AmoebeNachbarNr(int, int);
+void AmoebeUmwandeln(int, int);
+void ContinueMoving(int, int);
+void Bang(int, int);
+void InitMovDir(int, int);
+void InitAmoebaNr(int, int);
+int NewHiScore(void);
+
+void TestIfGoodThingHitsBadThing(int, int, int);
+void TestIfBadThingHitsGoodThing(int, int, int);
+void TestIfPlayerTouchesBadThing(int, int);
+void TestIfPlayerRunsIntoBadThing(int, int, int);
+void TestIfBadThingTouchesPlayer(int, int);
+void TestIfBadThingRunsIntoPlayer(int, int, int);
+void TestIfFriendTouchesBadThing(int, int);
+void TestIfBadThingTouchesFriend(int, int);
+void TestIfBadThingTouchesOtherBadThing(int, int);
+void TestIfGoodThingGetsHitByBadThing(int, int, int);
+
+void KillPlayer(struct PlayerInfo *);
+void BuryPlayer(struct PlayerInfo *);
+void RemovePlayer(struct PlayerInfo *);
+
+static int getInvisibleActiveFromInvisibleElement(int);
+static int getInvisibleFromInvisibleActiveElement(int);
+
static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
+/* for detection of endless loops, caused by custom element programming */
+/* (using maximal playfield width x 10 is just a rough approximation) */
+#define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
+
+#define RECURSION_LOOP_DETECTION_START(e, rc) \
+{ \
+ if (recursion_loop_detected) \
+ return (rc); \
+ \
+ if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
+ { \
+ recursion_loop_detected = TRUE; \
+ recursion_loop_element = (e); \
+ } \
+ \
+ recursion_loop_depth++; \
+}
+
+#define RECURSION_LOOP_DETECTION_END() \
+{ \
+ recursion_loop_depth--; \
+}
+
+static int recursion_loop_depth;
+static boolean recursion_loop_detected;
+static boolean recursion_loop_element;
+
+static int map_player_action[MAX_PLAYERS];
+
/* ------------------------------------------------------------------------- */
/* definition of elements that automatically change to other elements after */
static void InitMagicBallDelay(int, int);
static void ActivateMagicBall(int, int);
-static void InitDiagonalMovingElement(int, int);
-
struct ChangingElementInfo
{
int element;
NULL,
NULL
},
+ {
+ EL_STEEL_EXIT_OPENING,
+ EL_STEEL_EXIT_OPEN,
+ 29,
+ NULL,
+ NULL,
+ NULL
+ },
+ {
+ EL_STEEL_EXIT_CLOSING,
+ EL_STEEL_EXIT_CLOSED,
+ 29,
+ NULL,
+ NULL,
+ NULL
+ },
+ {
+ EL_EM_EXIT_OPENING,
+ EL_EM_EXIT_OPEN,
+ 29,
+ NULL,
+ NULL,
+ NULL
+ },
+ {
+ EL_EM_EXIT_CLOSING,
+ EL_EMPTY,
+ 29,
+ NULL,
+ NULL,
+ NULL
+ },
+ {
+ EL_EM_STEEL_EXIT_OPENING,
+ EL_EM_STEEL_EXIT_OPEN,
+ 29,
+ NULL,
+ NULL,
+ NULL
+ },
+ {
+ EL_EM_STEEL_EXIT_CLOSING,
+ EL_STEELWALL,
+ 29,
+ NULL,
+ NULL,
+ NULL
+ },
{
EL_SP_EXIT_OPENING,
EL_SP_EXIT_OPEN,
RunTimegateWheel,
NULL
},
+ {
+ EL_DC_TIMEGATE_SWITCH_ACTIVE,
+ EL_DC_TIMEGATE_SWITCH,
+ 0,
+ InitTimegateWheel,
+ RunTimegateWheel,
+ NULL
+ },
{
EL_EMC_MAGIC_BALL_ACTIVE,
EL_EMC_MAGIC_BALL_ACTIVE,
0,
NULL,
NULL,
- InitDiagonalMovingElement
+ NULL,
},
{
{ EL_AMOEBA_DROPPING, 2 },
{ EL_QUICKSAND_FILLING, 1 },
{ EL_QUICKSAND_EMPTYING, 1 },
+ { EL_QUICKSAND_FAST_FILLING, 2 },
+ { EL_QUICKSAND_FAST_EMPTYING, 2 },
{ EL_MAGIC_WALL_FILLING, 2 },
- { EL_BD_MAGIC_WALL_FILLING, 2 },
{ EL_MAGIC_WALL_EMPTYING, 2 },
+ { EL_BD_MAGIC_WALL_FILLING, 2 },
{ EL_BD_MAGIC_WALL_EMPTYING, 2 },
+ { EL_DC_MAGIC_WALL_FILLING, 2 },
+ { EL_DC_MAGIC_WALL_EMPTYING, 2 },
{ EL_UNDEFINED, 0 },
};
(y) += playfield_scan_delta_y) \
for ((x) = playfield_scan_start_x; \
(x) >= 0 && (x) <= lev_fieldx - 1; \
- (x) += playfield_scan_delta_x) \
+ (x) += playfield_scan_delta_x)
+
+#ifdef DEBUG
+void DEBUG_SetMaximumDynamite()
+{
+ int i;
+
+ for (i = 0; i < MAX_INVENTORY_SIZE; i++)
+ if (local_player->inventory_size < MAX_INVENTORY_SIZE)
+ local_player->inventory_element[local_player->inventory_size++] =
+ EL_DYNAMITE;
+}
+#endif
static void InitPlayfieldScanModeVars()
{
static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
boolean init_game)
{
+ int player_nr = player->index_nr;
int move_delay = get_move_delay_from_stepsize(move_stepsize);
boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
if (init_game)
{
- player->move_delay = game.initial_move_delay;
- player->move_delay_value = game.initial_move_delay_value;
+ player->move_delay = game.initial_move_delay[player_nr];
+ player->move_delay_value = game.initial_move_delay_value[player_nr];
player->move_delay_value_next = -1;
void GetPlayerConfig()
{
+ GameFrameDelay = setup.game_frame_delay;
+
if (!audio.sound_available)
setup.sound_simple = FALSE;
InitJoysticks();
}
-static int getBeltNrFromBeltElement(int element)
-{
- return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
- element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
- element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
-}
-
-static int getBeltNrFromBeltActiveElement(int element)
-{
- return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
- element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
- element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
-}
-
-static int getBeltNrFromBeltSwitchElement(int element)
-{
- return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
- element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
- element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
-}
-
-static int getBeltDirNrFromBeltSwitchElement(int element)
+int GetElementFromGroupElement(int element)
{
- static int belt_base_element[4] =
+ if (IS_GROUP_ELEMENT(element))
{
- EL_CONVEYOR_BELT_1_SWITCH_LEFT,
- EL_CONVEYOR_BELT_2_SWITCH_LEFT,
- EL_CONVEYOR_BELT_3_SWITCH_LEFT,
- EL_CONVEYOR_BELT_4_SWITCH_LEFT
- };
+ struct ElementGroupInfo *group = element_info[element].group;
+ int last_anim_random_frame = gfx.anim_random_frame;
+ int element_pos;
- int belt_nr = getBeltNrFromBeltSwitchElement(element);
- int belt_dir_nr = element - belt_base_element[belt_nr];
+ if (group->choice_mode == ANIM_RANDOM)
+ gfx.anim_random_frame = RND(group->num_elements_resolved);
- return (belt_dir_nr % 3);
-}
+ element_pos = getAnimationFrame(group->num_elements_resolved, 1,
+ group->choice_mode, 0,
+ group->choice_pos);
-static int getBeltDirFromBeltSwitchElement(int element)
-{
- static int belt_move_dir[3] =
- {
- MV_LEFT,
- MV_NONE,
- MV_RIGHT
- };
+ if (group->choice_mode == ANIM_RANDOM)
+ gfx.anim_random_frame = last_anim_random_frame;
- int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
+ group->choice_pos++;
+
+ element = group->element_resolved[element_pos];
+ }
- return belt_move_dir[belt_dir_nr];
+ return element;
}
static void InitPlayerField(int x, int y, int element, boolean init_game)
}
else
{
+ stored_player[0].initial_element = element;
stored_player[0].use_murphy = TRUE;
+
+ if (!level.use_artwork_element[0])
+ stored_player[0].artwork_element = EL_SP_MURPHY;
}
Feld[x][y] = EL_PLAYER_1;
StorePlayer[x][y] = Feld[x][y];
+#if DEBUG_INIT_PLAYER
if (options.debug)
{
- printf("Player %d activated.\n", player->element_nr);
- printf("[Local player is %d and currently %s.]\n",
+ printf("- player element %d activated", player->element_nr);
+ printf(" (local player is %d and currently %s)\n",
local_player->element_nr,
local_player->active ? "active" : "not active");
}
}
+#endif
Feld[x][y] = EL_EMPTY;
player->jx = player->last_jx = x;
player->jy = player->last_jy = y;
}
+
+ if (!init_game)
+ {
+ int player_nr = GET_PLAYER_NR(element);
+ struct PlayerInfo *player = &stored_player[player_nr];
+
+ if (player->active && player->killed)
+ player->reanimated = TRUE; /* if player was just killed, reanimate him */
+ }
}
static void InitField(int x, int y, boolean init_game)
Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
break;
+ case EL_BUG:
case EL_BUG_RIGHT:
case EL_BUG_UP:
case EL_BUG_LEFT:
case EL_BUG_DOWN:
- case EL_BUG:
+ case EL_SPACESHIP:
case EL_SPACESHIP_RIGHT:
case EL_SPACESHIP_UP:
case EL_SPACESHIP_LEFT:
case EL_SPACESHIP_DOWN:
- case EL_SPACESHIP:
+ case EL_BD_BUTTERFLY:
case EL_BD_BUTTERFLY_RIGHT:
case EL_BD_BUTTERFLY_UP:
case EL_BD_BUTTERFLY_LEFT:
case EL_BD_BUTTERFLY_DOWN:
- case EL_BD_BUTTERFLY:
+ case EL_BD_FIREFLY:
case EL_BD_FIREFLY_RIGHT:
case EL_BD_FIREFLY_UP:
case EL_BD_FIREFLY_LEFT:
case EL_BD_FIREFLY_DOWN:
- case EL_BD_FIREFLY:
case EL_PACMAN_RIGHT:
case EL_PACMAN_UP:
case EL_PACMAN_LEFT:
case EL_PACMAN_DOWN:
case EL_YAMYAM:
+ case EL_YAMYAM_LEFT:
+ case EL_YAMYAM_RIGHT:
+ case EL_YAMYAM_UP:
+ case EL_YAMYAM_DOWN:
case EL_DARK_YAMYAM:
case EL_ROBOT:
case EL_PACMAN:
case EL_SP_SNIKSNAK:
case EL_SP_ELECTRON:
+ case EL_MOLE:
case EL_MOLE_LEFT:
case EL_MOLE_RIGHT:
case EL_MOLE_UP:
case EL_MOLE_DOWN:
- case EL_MOLE:
InitMovDir(x, y);
break;
MovDelay[x][y] = 96;
break;
+ case EL_EM_DYNAMITE_ACTIVE:
+ MovDelay[x][y] = 32;
+ break;
+
case EL_LAMP:
local_player->lights_still_needed++;
break;
}
break;
- case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
- if (init_game)
- Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
- break;
-
case EL_LIGHT_SWITCH_ACTIVE:
if (init_game)
game.light_time_left = level.time_light * FRAMES_PER_SECOND;
break;
+ case EL_INVISIBLE_STEELWALL:
+ case EL_INVISIBLE_WALL:
+ case EL_INVISIBLE_SAND:
+ if (game.light_time_left > 0 ||
+ game.lenses_time_left > 0)
+ Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
+ break;
+
case EL_EMC_MAGIC_BALL:
if (game.ball_state)
Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
break;
+ case EL_TRIGGER_PLAYER:
+ case EL_TRIGGER_ELEMENT:
+ case EL_TRIGGER_CE_VALUE:
+ case EL_TRIGGER_CE_SCORE:
+ case EL_SELF:
+ case EL_ANY_ELEMENT:
+ case EL_CURRENT_CE_VALUE:
+ case EL_CURRENT_CE_SCORE:
+ case EL_PREV_CE_1:
+ case EL_PREV_CE_2:
+ case EL_PREV_CE_3:
+ case EL_PREV_CE_4:
+ case EL_PREV_CE_5:
+ case EL_PREV_CE_6:
+ case EL_PREV_CE_7:
+ case EL_PREV_CE_8:
+ case EL_NEXT_CE_1:
+ case EL_NEXT_CE_2:
+ case EL_NEXT_CE_3:
+ case EL_NEXT_CE_4:
+ case EL_NEXT_CE_5:
+ case EL_NEXT_CE_6:
+ case EL_NEXT_CE_7:
+ case EL_NEXT_CE_8:
+ /* reference elements should not be used on the playfield */
+ Feld[x][y] = EL_EMPTY;
+ break;
+
default:
-#if 1
if (IS_CUSTOM_ELEMENT(element))
{
if (CAN_MOVE(element))
InitMovDir(x, y);
-#if USE_NEW_CUSTOM_VALUE
if (!element_info[element].use_last_ce_value || init_game)
- CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]);
-#endif
+ CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
}
-#else
- if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
- InitMovDir(x, y);
-#endif
else if (IS_GROUP_ELEMENT(element))
{
- struct ElementGroupInfo *group = element_info[element].group;
- int last_anim_random_frame = gfx.anim_random_frame;
- int element_pos;
-
- if (group->choice_mode == ANIM_RANDOM)
- gfx.anim_random_frame = RND(group->num_elements_resolved);
-
- element_pos = getAnimationFrame(group->num_elements_resolved, 1,
- group->choice_mode, 0,
- group->choice_pos);
-
- if (group->choice_mode == ANIM_RANDOM)
- gfx.anim_random_frame = last_anim_random_frame;
-
- group->choice_pos++;
-
- Feld[x][y] = group->element_resolved[element_pos];
+ Feld[x][y] = GetElementFromGroupElement(element);
InitField(x, y, init_game);
}
+
break;
}
-#if 1
if (!init_game)
CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
-#endif
-
-#if 0
-
-#if USE_NEW_CUSTOM_VALUE
-
-#if 1
- CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]);
-#else
- CustomValue[x][y] = element_info[Feld[x][y]].custom_value_initial;
-#endif
-
-#endif
-
-#endif
}
-static inline void InitField_WithBug1(int x, int y, boolean init_game)
+inline static void InitField_WithBug1(int x, int y, boolean init_game)
{
InitField(x, y, init_game);
InitMovDir(x, y);
}
-static inline void InitField_WithBug2(int x, int y, boolean init_game)
+inline static void InitField_WithBug2(int x, int y, boolean init_game)
{
int old_element = Feld[x][y];
*/
}
-inline void DrawGameValue_Emeralds(int value)
+static int get_key_element_from_nr(int key_nr)
{
- DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
+ int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
+ level.game_engine_type == GAME_ENGINE_TYPE_EM ?
+ EL_EM_KEY_1 : EL_KEY_1);
+
+ return key_base_element + key_nr;
}
-inline void DrawGameValue_Dynamite(int value)
+static int get_next_dropped_element(struct PlayerInfo *player)
{
- DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
+ return (player->inventory_size > 0 ?
+ player->inventory_element[player->inventory_size - 1] :
+ player->inventory_infinite_element != EL_UNDEFINED ?
+ player->inventory_infinite_element :
+ player->dynabombs_left > 0 ?
+ EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
+ EL_UNDEFINED);
}
-inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
+static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
{
- int i;
+ /* pos >= 0: get element from bottom of the stack;
+ pos < 0: get element from top of the stack */
- /* currently only 4 of 8 possible keys are displayed */
- for (i = 0; i < STD_NUM_KEYS; i++)
+ if (pos < 0)
{
- if (key[i])
- DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
- el2edimg(EL_KEY_1 + i));
- else
- BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
- DOOR_GFX_PAGEX5 + XX_KEYS + i * MINI_TILEX, YY_KEYS,
- MINI_TILEX, MINI_TILEY, DX_KEYS + i * MINI_TILEX, DY_KEYS);
+ int min_inventory_size = -pos;
+ int inventory_pos = player->inventory_size - min_inventory_size;
+ int min_dynabombs_left = min_inventory_size - player->inventory_size;
+
+ return (player->inventory_size >= min_inventory_size ?
+ player->inventory_element[inventory_pos] :
+ player->inventory_infinite_element != EL_UNDEFINED ?
+ player->inventory_infinite_element :
+ player->dynabombs_left >= min_dynabombs_left ?
+ EL_DYNABOMB_PLAYER_1 + player->index_nr :
+ EL_UNDEFINED);
}
-}
+ else
+ {
+ int min_dynabombs_left = pos + 1;
+ int min_inventory_size = pos + 1 - player->dynabombs_left;
+ int inventory_pos = pos - player->dynabombs_left;
-inline void DrawGameValue_Score(int value)
-{
- DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
+ return (player->inventory_infinite_element != EL_UNDEFINED ?
+ player->inventory_infinite_element :
+ player->dynabombs_left >= min_dynabombs_left ?
+ EL_DYNABOMB_PLAYER_1 + player->index_nr :
+ player->inventory_size >= min_inventory_size ?
+ player->inventory_element[inventory_pos] :
+ EL_UNDEFINED);
+ }
}
-inline void DrawGameValue_Time(int value)
+static int compareGamePanelOrderInfo(const void *object1, const void *object2)
{
- if (value < 1000)
- DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
+ const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
+ const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
+ int compare_result;
+
+ if (gpo1->sort_priority != gpo2->sort_priority)
+ compare_result = gpo1->sort_priority - gpo2->sort_priority;
else
- DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
+ compare_result = gpo1->nr - gpo2->nr;
+
+ return compare_result;
}
-inline void DrawGameValue_Level(int value)
+void InitGameControlValues()
{
- if (level_nr < 100)
- DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
- else
+ int i;
+
+ for (i = 0; game_panel_controls[i].nr != -1; i++)
{
- /* misuse area for displaying emeralds to draw bigger level number */
- DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
- int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
+ struct GamePanelControlInfo *gpc = &game_panel_controls[i];
+ struct GamePanelOrderInfo *gpo = &game_panel_order[i];
+ struct TextPosInfo *pos = gpc->pos;
+ int nr = gpc->nr;
+ int type = gpc->type;
+
+ if (nr != i)
+ {
+ Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
+ Error(ERR_EXIT, "this should not happen -- please debug");
+ }
- /* now copy it to the area for displaying level number */
- BlitBitmap(drawto, drawto,
- DX_EMERALDS, DY_EMERALDS + 1,
- getFontWidth(FONT_LEVEL_NUMBER) * 3,
- getFontHeight(FONT_LEVEL_NUMBER) - 1,
- DX_LEVEL - 1, DY_LEVEL + 1);
+ /* force update of game controls after initialization */
+ gpc->value = gpc->last_value = -1;
+ gpc->frame = gpc->last_frame = -1;
+ gpc->gfx_frame = -1;
- /* restore the area for displaying emeralds */
- DrawGameValue_Emeralds(local_player->gems_still_needed);
+ /* determine panel value width for later calculation of alignment */
+ if (type == TYPE_INTEGER || type == TYPE_STRING)
+ {
+ pos->width = pos->size * getFontWidth(pos->font);
+ pos->height = getFontHeight(pos->font);
+ }
+ else if (type == TYPE_ELEMENT)
+ {
+ pos->width = pos->size;
+ pos->height = pos->size;
+ }
- /* yes, this is all really ugly :-) */
+ /* fill structure for game panel draw order */
+ gpo->nr = gpc->nr;
+ gpo->sort_priority = pos->sort_priority;
}
+
+ /* sort game panel controls according to sort_priority and control number */
+ qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
+ sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
}
-void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
- int key_bits)
+void UpdatePlayfieldElementCount()
{
- int key[MAX_NUM_KEYS];
- int i;
+ boolean use_element_count = FALSE;
+ int i, j, x, y;
- for (i = 0; i < MAX_NUM_KEYS; i++)
- key[i] = key_bits & (1 << i);
+ /* first check if it is needed at all to calculate playfield element count */
+ for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
+ if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
+ use_element_count = TRUE;
- DrawGameValue_Level(level_nr);
+ if (!use_element_count)
+ return;
- DrawGameValue_Emeralds(emeralds);
- DrawGameValue_Dynamite(dynamite);
- DrawGameValue_Score(score);
- DrawGameValue_Time(time);
+ for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+ element_info[i].element_count = 0;
- DrawGameValue_Keys(key);
-}
+ SCAN_PLAYFIELD(x, y)
+ {
+ element_info[Feld[x][y]].element_count++;
+ }
-void DrawGameDoorValues()
-{
- int i;
+ for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
+ for (j = 0; j < MAX_NUM_ELEMENTS; j++)
+ if (IS_IN_GROUP(j, i))
+ element_info[EL_GROUP_START + i].element_count +=
+ element_info[j].element_count;
+}
+
+void UpdateGameControlValues()
+{
+ int i, k;
+ int time = (local_player->LevelSolved ?
+ local_player->LevelSolved_CountingTime :
+ level.game_engine_type == GAME_ENGINE_TYPE_EM ?
+ level.native_em_level->lev->time :
+ level.game_engine_type == GAME_ENGINE_TYPE_SP ?
+ level.native_sp_level->game_sp->time_played :
+ game.no_time_limit ? TimePlayed : TimeLeft);
+ int score = (local_player->LevelSolved ?
+ local_player->LevelSolved_CountingScore :
+ level.game_engine_type == GAME_ENGINE_TYPE_EM ?
+ level.native_em_level->lev->score :
+ level.game_engine_type == GAME_ENGINE_TYPE_SP ?
+ level.native_sp_level->game_sp->score :
+ local_player->score);
+ int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
+ level.native_em_level->lev->required :
+ level.game_engine_type == GAME_ENGINE_TYPE_SP ?
+ level.native_sp_level->game_sp->infotrons_still_needed :
+ local_player->gems_still_needed);
+ int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
+ level.native_em_level->lev->required > 0 :
+ level.game_engine_type == GAME_ENGINE_TYPE_SP ?
+ level.native_sp_level->game_sp->infotrons_still_needed > 0 :
+ local_player->gems_still_needed > 0 ||
+ local_player->sokobanfields_still_needed > 0 ||
+ local_player->lights_still_needed > 0);
+
+ UpdatePlayfieldElementCount();
+
+ /* update game panel control values */
+
+ game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
+ game_panel_controls[GAME_PANEL_GEMS].value = gems;
+
+ game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
+ for (i = 0; i < MAX_NUM_KEYS; i++)
+ game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
+ game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
+ game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
- if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+ if (game.centered_player_nr == -1)
{
- DrawGameDoorValues_EM();
+ for (i = 0; i < MAX_PLAYERS; i++)
+ {
+ /* only one player in Supaplex game engine */
+ if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
+ break;
- return;
- }
+ for (k = 0; k < MAX_NUM_KEYS; k++)
+ {
+ if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+ {
+ if (level.native_em_level->ply[i]->keys & (1 << k))
+ game_panel_controls[GAME_PANEL_KEY_1 + k].value =
+ get_key_element_from_nr(k);
+ }
+ else if (stored_player[i].key[k])
+ game_panel_controls[GAME_PANEL_KEY_1 + k].value =
+ get_key_element_from_nr(k);
+ }
- DrawGameValue_Level(level_nr);
+ if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+ game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
+ level.native_em_level->ply[i]->dynamite;
+ else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+ game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
+ level.native_sp_level->game_sp->red_disk_count;
+ else
+ game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
+ stored_player[i].inventory_size;
- DrawGameValue_Emeralds(local_player->gems_still_needed);
- DrawGameValue_Dynamite(local_player->inventory_size);
- DrawGameValue_Score(local_player->score);
- DrawGameValue_Time(TimeLeft);
+ if (stored_player[i].num_white_keys > 0)
+ game_panel_controls[GAME_PANEL_KEY_WHITE].value =
+ EL_DC_KEY_WHITE;
- for (i = 0; i < MAX_PLAYERS; i++)
- DrawGameValue_Keys(stored_player[i].key);
-}
+ game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
+ stored_player[i].num_white_keys;
+ }
+ }
+ else
+ {
+ int player_nr = game.centered_player_nr;
-#if 0
-static void resolve_group_element(int group_element, int recursion_depth)
-{
- static int group_nr;
- static struct ElementGroupInfo *group;
- struct ElementGroupInfo *actual_group = element_info[group_element].group;
- int i;
+ for (k = 0; k < MAX_NUM_KEYS; k++)
+ {
+ if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+ {
+ if (level.native_em_level->ply[player_nr]->keys & (1 << k))
+ game_panel_controls[GAME_PANEL_KEY_1 + k].value =
+ get_key_element_from_nr(k);
+ }
+ else if (stored_player[player_nr].key[k])
+ game_panel_controls[GAME_PANEL_KEY_1 + k].value =
+ get_key_element_from_nr(k);
+ }
- if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
- {
- Error(ERR_WARN, "recursion too deep when resolving group element %d",
- group_element - EL_GROUP_START + 1);
+ if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+ game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
+ level.native_em_level->ply[player_nr]->dynamite;
+ else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+ game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
+ level.native_sp_level->game_sp->red_disk_count;
+ else
+ game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
+ stored_player[player_nr].inventory_size;
- /* replace element which caused too deep recursion by question mark */
- group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
+ if (stored_player[player_nr].num_white_keys > 0)
+ game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
- return;
+ game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
+ stored_player[player_nr].num_white_keys;
}
- if (recursion_depth == 0) /* initialization */
+ for (i = 0; i < NUM_PANEL_INVENTORY; i++)
{
- group = element_info[group_element].group;
- group_nr = group_element - EL_GROUP_START;
-
- group->num_elements_resolved = 0;
- group->choice_pos = 0;
+ game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
+ get_inventory_element_from_pos(local_player, i);
+ game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
+ get_inventory_element_from_pos(local_player, -i - 1);
}
- for (i = 0; i < actual_group->num_elements; i++)
- {
- int element = actual_group->element[i];
+ game_panel_controls[GAME_PANEL_SCORE].value = score;
+ game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
- if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
- break;
+ game_panel_controls[GAME_PANEL_TIME].value = time;
- if (IS_GROUP_ELEMENT(element))
- resolve_group_element(element, recursion_depth + 1);
- else
- {
- group->element_resolved[group->num_elements_resolved++] = element;
- element_info[element].in_group[group_nr] = TRUE;
- }
+ game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
+ game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
+ game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
+
+ game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
+
+ game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
+ (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
+ EL_EMPTY);
+ game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
+ local_player->shield_normal_time_left;
+ game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
+ (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
+ EL_EMPTY);
+ game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
+ local_player->shield_deadly_time_left;
+
+ game_panel_controls[GAME_PANEL_EXIT].value =
+ (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
+
+ game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
+ (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
+ game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
+ (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
+ EL_EMC_MAGIC_BALL_SWITCH);
+
+ game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
+ (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
+ game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
+ game.light_time_left;
+
+ game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
+ (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
+ game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
+ game.timegate_time_left;
+
+ game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
+ EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
+
+ game_panel_controls[GAME_PANEL_EMC_LENSES].value =
+ (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
+ game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
+ game.lenses_time_left;
+
+ game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
+ (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
+ game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
+ game.magnify_time_left;
+
+ game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
+ (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
+ game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
+ game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
+ game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
+ EL_BALLOON_SWITCH_NONE);
+
+ game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
+ local_player->dynabomb_count;
+ game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
+ local_player->dynabomb_size;
+ game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
+ (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
+
+ game_panel_controls[GAME_PANEL_PENGUINS].value =
+ local_player->friends_still_needed;
+
+ game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
+ local_player->sokobanfields_still_needed;
+ game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
+ local_player->sokobanfields_still_needed;
+
+ game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
+ (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
+
+ for (i = 0; i < NUM_BELTS; i++)
+ {
+ game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
+ (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
+ EL_CONVEYOR_BELT_1_MIDDLE) + i;
+ game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
+ getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
}
-}
-#endif
-/*
- =============================================================================
- InitGameEngine()
- -----------------------------------------------------------------------------
- initialize game engine due to level / tape version number
- =============================================================================
-*/
+ game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
+ (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
+ game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
+ game.magic_wall_time_left;
-static void InitGameEngine()
-{
- int i, j, k, l, x, y;
+ game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
+ local_player->gravity;
- /* set game engine from tape file when re-playing, else from level file */
- game.engine_version = (tape.playing ? tape.engine_version :
- level.game_version);
+ for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
+ game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
- /* ---------------------------------------------------------------------- */
- /* set flags for bugs and changes according to active game engine version */
- /* ---------------------------------------------------------------------- */
+ for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
+ game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
+ (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
+ game.panel.element[i].id : EL_UNDEFINED);
- /*
- Summary of bugfix/change:
- Fixed handling for custom elements that change when pushed by the player.
+ for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
+ game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
+ (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
+ element_info[game.panel.element_count[i].id].element_count : 0);
- Fixed/changed in version:
- 3.1.0
+ for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
+ game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
+ (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
+ element_info[game.panel.ce_score[i].id].collect_score : 0);
- Description:
- Before 3.1.0, custom elements that "change when pushing" changed directly
- after the player started pushing them (until then handled in "DigField()").
- Since 3.1.0, these custom elements are not changed until the "pushing"
- move of the element is finished (now handled in "ContinueMoving()").
+ for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
+ game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
+ (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
+ element_info[game.panel.ce_score_element[i].id].collect_score :
+ EL_UNDEFINED);
- Affected levels/tapes:
- The first condition is generally needed for all levels/tapes before version
- 3.1.0, which might use the old behaviour before it was changed; known tapes
- that are affected are some tapes from the level set "Walpurgis Gardens" by
+ game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
+ game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
+ game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
+
+ /* update game panel control frames */
+
+ for (i = 0; game_panel_controls[i].nr != -1; i++)
+ {
+ struct GamePanelControlInfo *gpc = &game_panel_controls[i];
+
+ if (gpc->type == TYPE_ELEMENT)
+ {
+ if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
+ {
+ int last_anim_random_frame = gfx.anim_random_frame;
+ int element = gpc->value;
+ int graphic = el2panelimg(element);
+
+ if (gpc->value != gpc->last_value)
+ {
+ gpc->gfx_frame = 0;
+ gpc->gfx_random = INIT_GFX_RANDOM();
+ }
+ else
+ {
+ gpc->gfx_frame++;
+
+ if (ANIM_MODE(graphic) == ANIM_RANDOM &&
+ IS_NEXT_FRAME(gpc->gfx_frame, graphic))
+ gpc->gfx_random = INIT_GFX_RANDOM();
+ }
+
+ if (ANIM_MODE(graphic) == ANIM_RANDOM)
+ gfx.anim_random_frame = gpc->gfx_random;
+
+ if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
+ gpc->gfx_frame = element_info[element].collect_score;
+
+ gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
+ gpc->gfx_frame);
+
+ if (ANIM_MODE(graphic) == ANIM_RANDOM)
+ gfx.anim_random_frame = last_anim_random_frame;
+ }
+ }
+ }
+}
+
+void DisplayGameControlValues()
+{
+ boolean redraw_panel = FALSE;
+ int i;
+
+ for (i = 0; game_panel_controls[i].nr != -1; i++)
+ {
+ struct GamePanelControlInfo *gpc = &game_panel_controls[i];
+
+ if (PANEL_DEACTIVATED(gpc->pos))
+ continue;
+
+ if (gpc->value == gpc->last_value &&
+ gpc->frame == gpc->last_frame)
+ continue;
+
+ redraw_panel = TRUE;
+ }
+
+ if (!redraw_panel)
+ return;
+
+ /* copy default game door content to main double buffer */
+
+ /* !!! CHECK AGAIN !!! */
+ SetPanelBackground();
+ // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
+ DrawBackground(DX, DY, DXSIZE, DYSIZE);
+
+ /* redraw game control buttons */
+ RedrawGameButtons();
+
+ game_status = GAME_MODE_PSEUDO_PANEL;
+
+ for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
+ {
+ int nr = game_panel_order[i].nr;
+ struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
+ struct TextPosInfo *pos = gpc->pos;
+ int type = gpc->type;
+ int value = gpc->value;
+ int frame = gpc->frame;
+ int size = pos->size;
+ int font = pos->font;
+ boolean draw_masked = pos->draw_masked;
+ int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
+
+ if (PANEL_DEACTIVATED(pos))
+ continue;
+
+ gpc->last_value = value;
+ gpc->last_frame = frame;
+
+ if (type == TYPE_INTEGER)
+ {
+ if (nr == GAME_PANEL_LEVEL_NUMBER ||
+ nr == GAME_PANEL_TIME)
+ {
+ boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
+
+ if (use_dynamic_size) /* use dynamic number of digits */
+ {
+ int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
+ int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
+ int size2 = size1 + 1;
+ int font1 = pos->font;
+ int font2 = pos->font_alt;
+
+ size = (value < value_change ? size1 : size2);
+ font = (value < value_change ? font1 : font2);
+ }
+ }
+
+ /* correct text size if "digits" is zero or less */
+ if (size <= 0)
+ size = strlen(int2str(value, size));
+
+ /* dynamically correct text alignment */
+ pos->width = size * getFontWidth(font);
+
+ DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
+ int2str(value, size), font, mask_mode);
+ }
+ else if (type == TYPE_ELEMENT)
+ {
+ int element, graphic;
+ Bitmap *src_bitmap;
+ int src_x, src_y;
+ int width, height;
+ int dst_x = PANEL_XPOS(pos);
+ int dst_y = PANEL_YPOS(pos);
+
+ if (value != EL_UNDEFINED && value != EL_EMPTY)
+ {
+ element = value;
+ graphic = el2panelimg(value);
+
+ // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
+
+ if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
+ size = TILESIZE;
+
+ getSizedGraphicSource(graphic, frame, size, &src_bitmap,
+ &src_x, &src_y);
+
+ width = graphic_info[graphic].width * size / TILESIZE;
+ height = graphic_info[graphic].height * size / TILESIZE;
+
+ if (draw_masked)
+ BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
+ dst_x, dst_y);
+ else
+ BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
+ dst_x, dst_y);
+ }
+ }
+ else if (type == TYPE_STRING)
+ {
+ boolean active = (value != 0);
+ char *state_normal = "off";
+ char *state_active = "on";
+ char *state = (active ? state_active : state_normal);
+ char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
+ nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
+ nr == GAME_PANEL_LEVEL_NAME ? level.name :
+ nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
+
+ if (nr == GAME_PANEL_GRAVITY_STATE)
+ {
+ int font1 = pos->font; /* (used for normal state) */
+ int font2 = pos->font_alt; /* (used for active state) */
+
+ font = (active ? font2 : font1);
+ }
+
+ if (s != NULL)
+ {
+ char *s_cut;
+
+ if (size <= 0)
+ {
+ /* don't truncate output if "chars" is zero or less */
+ size = strlen(s);
+
+ /* dynamically correct text alignment */
+ pos->width = size * getFontWidth(font);
+ }
+
+ s_cut = getStringCopyN(s, size);
+
+ DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
+ s_cut, font, mask_mode);
+
+ free(s_cut);
+ }
+ }
+
+ redraw_mask |= REDRAW_DOOR_1;
+ }
+
+ game_status = GAME_MODE_PLAYING;
+}
+
+void UpdateAndDisplayGameControlValues()
+{
+ if (tape.deactivate_display)
+ return;
+
+ UpdateGameControlValues();
+ DisplayGameControlValues();
+}
+
+void UpdateGameDoorValues()
+{
+ UpdateGameControlValues();
+}
+
+void DrawGameDoorValues()
+{
+ DisplayGameControlValues();
+}
+
+
+/*
+ =============================================================================
+ InitGameEngine()
+ -----------------------------------------------------------------------------
+ initialize game engine due to level / tape version number
+ =============================================================================
+*/
+
+static void InitGameEngine()
+{
+ int i, j, k, l, x, y;
+
+ /* set game engine from tape file when re-playing, else from level file */
+ game.engine_version = (tape.playing ? tape.engine_version :
+ level.game_version);
+
+ /* set single or multi-player game mode (needed for re-playing tapes) */
+ game.team_mode = setup.team_mode;
+
+ if (tape.playing)
+ {
+ int num_players = 0;
+
+ for (i = 0; i < MAX_PLAYERS; i++)
+ if (tape.player_participates[i])
+ num_players++;
+
+ /* multi-player tapes contain input data for more than one player */
+ game.team_mode = (num_players > 1);
+ }
+
+ /* ---------------------------------------------------------------------- */
+ /* set flags for bugs and changes according to active game engine version */
+ /* ---------------------------------------------------------------------- */
+
+ /*
+ Summary of bugfix/change:
+ Fixed handling for custom elements that change when pushed by the player.
+
+ Fixed/changed in version:
+ 3.1.0
+
+ Description:
+ Before 3.1.0, custom elements that "change when pushing" changed directly
+ after the player started pushing them (until then handled in "DigField()").
+ Since 3.1.0, these custom elements are not changed until the "pushing"
+ move of the element is finished (now handled in "ContinueMoving()").
+
+ Affected levels/tapes:
+ The first condition is generally needed for all levels/tapes before version
+ 3.1.0, which might use the old behaviour before it was changed; known tapes
+ that are affected are some tapes from the level set "Walpurgis Gardens" by
Jamie Cullen.
The second condition is an exception from the above case and is needed for
the special case of tapes recorded with game (not engine!) version 3.1.0 or
game.use_block_last_field_bug =
(game.engine_version < VERSION_IDENT(3,1,1,0));
- /*
- Summary of bugfix/change:
- Changed behaviour of CE changes with multiple changes per single frame.
-
- Fixed/changed in version:
- 3.2.0-6
-
- Description:
- Before 3.2.0-6, only one single CE change was allowed in each engine frame.
- This resulted in race conditions where CEs seem to behave strange in some
- situations (where triggered CE changes were just skipped because there was
- already a CE change on that tile in the playfield in that engine frame).
- Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
- (The number of changes per frame must be limited in any case, because else
- it is easily possible to define CE changes that would result in an infinite
- loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
- should be set large enough so that it would only be reached in cases where
- the corresponding CE change conditions run into a loop. Therefore, it seems
- to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
- maximal number of change pages for custom elements.)
-
- Affected levels/tapes:
- Probably many.
- */
+ /* ---------------------------------------------------------------------- */
-#if USE_ONLY_ONE_CHANGE_PER_FRAME
+ /* set maximal allowed number of custom element changes per game frame */
game.max_num_changes_per_frame = 1;
-#else
- game.max_num_changes_per_frame =
- (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
-#endif
-
- /* ---------------------------------------------------------------------- */
/* default scan direction: scan playfield from top/left to bottom/right */
InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
printf(" => game.engine_version == %06d\n", game.engine_version);
#endif
-#if 0
- /* ---------- recursively resolve group elements ------------------------- */
-
- for (i = 0; i < MAX_NUM_ELEMENTS; i++)
- for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
- element_info[i].in_group[j] = FALSE;
-
- for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
- resolve_group_element(EL_GROUP_START + i, 0);
-#endif
-
/* ---------- initialize player's initial move delay --------------------- */
-#if 1
/* dynamically adjust player properties according to level information */
- game.initial_move_delay_value =
- get_move_delay_from_stepsize(level.initial_player_stepsize);
-#else
- /* dynamically adjust player properties according to level information */
- game.initial_move_delay_value =
- (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
-#endif
+ for (i = 0; i < MAX_PLAYERS; i++)
+ game.initial_move_delay_value[i] =
+ get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
/* dynamically adjust player properties according to game engine version */
- game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
- game.initial_move_delay_value : 0);
+ for (i = 0; i < MAX_PLAYERS; i++)
+ game.initial_move_delay[i] =
+ (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
+ game.initial_move_delay_value[i] : 0);
/* ---------- initialize player's initial push delay --------------------- */
SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
}
- /* ---------- initialize internal run-time variables ------------- */
+ /* ---------- initialize internal run-time variables --------------------- */
for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
{
}
}
+ /* ---------- initialize reference elements in change conditions --------- */
+
+ for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
+ {
+ int element = EL_CUSTOM_START + i;
+ struct ElementInfo *ei = &element_info[element];
+
+ for (j = 0; j < ei->num_change_pages; j++)
+ {
+ int trigger_element = ei->change_page[j].initial_trigger_element;
+
+ if (trigger_element >= EL_PREV_CE_8 &&
+ trigger_element <= EL_NEXT_CE_8)
+ trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
+
+ ei->change_page[j].trigger_element = trigger_element;
+ }
+ }
+
/* ---------- initialize run-time trigger player and element ------------- */
for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
for (j = 0; j < ei->num_change_pages; j++)
{
ei->change_page[j].actual_trigger_element = EL_EMPTY;
- ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
+ ei->change_page[j].actual_trigger_player = EL_EMPTY;
+ ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
ei->change_page[j].actual_trigger_ce_value = 0;
+ ei->change_page[j].actual_trigger_ce_score = 0;
}
}
for (l = 0; l < group->num_elements_resolved; l++)
trigger_events[group->element_resolved[l]][k] = TRUE;
}
+ else if (trigger_element == EL_ANY_ELEMENT)
+ for (l = 0; l < MAX_NUM_ELEMENTS; l++)
+ trigger_events[l][k] = TRUE;
else
trigger_events[trigger_element][k] = TRUE;
}
{
if (!IS_CUSTOM_ELEMENT(i))
{
- element_info[i].push_delay_fixed = game.default_push_delay_fixed;
- element_info[i].push_delay_random = game.default_push_delay_random;
+ /* set default push delay values (corrected since version 3.0.7-1) */
+ if (game.engine_version < VERSION_IDENT(3,0,7,1))
+ {
+ element_info[i].push_delay_fixed = 2;
+ element_info[i].push_delay_random = 8;
+ }
+ else
+ {
+ element_info[i].push_delay_fixed = 8;
+ element_info[i].push_delay_random = 8;
+ }
}
}
EL_EMPTY);
}
}
+
+ /* ---------- initialize recursion detection ------------------------------ */
+ recursion_loop_depth = 0;
+ recursion_loop_detected = FALSE;
+ recursion_loop_element = EL_UNDEFINED;
+
+ /* ---------- initialize graphics engine ---------------------------------- */
+ game.scroll_delay_value =
+ (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
+ setup.scroll_delay ? setup.scroll_delay_value : 0);
+ game.scroll_delay_value =
+ MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
+
+ /* ---------- initialize game engine snapshots ---------------------------- */
+ for (i = 0; i < MAX_PLAYERS; i++)
+ game.snapshot.last_action[i] = 0;
+ game.snapshot.changed_action = FALSE;
+ game.snapshot.collected_item = FALSE;
+ game.snapshot.mode =
+ (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
+ SNAPSHOT_MODE_EVERY_STEP :
+ strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
+ SNAPSHOT_MODE_EVERY_MOVE :
+ strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
+ SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
}
int get_num_special_action(int element, int action_first, int action_last)
return num_special_action;
}
+
/*
=============================================================================
InitGame()
void InitGame()
{
+ int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
+ int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
+ int fade_mask = REDRAW_FIELD;
+
boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
+ int initial_move_dir = MV_DOWN;
int i, j, x, y;
+ // required here to update video display before fading (FIX THIS)
+ DrawMaskedBorder(REDRAW_DOOR_2);
+
+ game_status = GAME_MODE_PLAYING;
+
+ if (!game.restart_level)
+ CloseDoor(DOOR_CLOSE_1);
+
+ /* needed if different viewport properties defined for playing */
+ ChangeViewportPropertiesIfNeeded();
+
+ if (level_editor_test_game)
+ FadeSkipNextFadeIn();
+ else
+ FadeSetEnterScreen();
+
+ if (CheckIfGlobalBorderHasChanged())
+ fade_mask = REDRAW_ALL;
+
+ FadeOut(fade_mask);
+
+ OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
+
+ ClearField();
+
+ DrawCompleteVideoDisplay();
+
InitGameEngine();
+ InitGameControlValues();
/* don't play tapes over network */
network_playing = (options.network && !tape.playing);
player->present = FALSE;
player->active = FALSE;
+ player->mapped = FALSE;
+
+ player->killed = FALSE;
+ player->reanimated = FALSE;
player->action = 0;
player->effective_action = 0;
player->programmed_action = 0;
player->score = 0;
+ player->score_final = 0;
+
player->gems_still_needed = level.gems_needed;
player->sokobanfields_still_needed = 0;
player->lights_still_needed = 0;
for (j = 0; j < MAX_NUM_KEYS; j++)
player->key[j] = FALSE;
+ player->num_white_keys = 0;
+
player->dynabomb_count = 0;
player->dynabomb_size = 1;
player->dynabombs_left = 0;
player->dynabomb_xl = FALSE;
- player->MovDir = MV_NONE;
+ player->MovDir = initial_move_dir;
player->MovPos = 0;
player->GfxPos = 0;
- player->GfxDir = MV_NONE;
+ player->GfxDir = initial_move_dir;
player->GfxAction = ACTION_DEFAULT;
player->Frame = 0;
player->StepFrame = 0;
- player->use_murphy = FALSE;
+ player->initial_element = player->element_nr;
player->artwork_element =
(level.use_artwork_element[i] ? level.artwork_element[i] :
player->element_nr);
+ player->use_murphy = FALSE;
player->block_last_field = FALSE; /* initialized in InitPlayerField() */
player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
+ player->gravity = level.initial_player_gravity[i];
+
player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
player->actual_frame_counter = 0;
player->step_counter = 0;
- player->last_move_dir = MV_NONE;
+ player->last_move_dir = initial_move_dir;
+
+ player->is_active = FALSE;
player->is_waiting = FALSE;
player->is_moving = FALSE;
player->is_pushing = FALSE;
player->is_switching = FALSE;
player->is_dropping = FALSE;
+ player->is_dropping_pressed = FALSE;
player->is_bored = FALSE;
player->is_sleeping = FALSE;
player->anim_delay_counter = 0;
player->post_delay_counter = 0;
+ player->dir_waiting = initial_move_dir;
player->action_waiting = ACTION_DEFAULT;
player->last_action_waiting = ACTION_DEFAULT;
player->special_action_bored = ACTION_DEFAULT;
player->special_action_sleeping = ACTION_DEFAULT;
- /* set number of special actions for bored and sleeping animation */
- player->num_special_action_bored =
- get_num_special_action(player->artwork_element,
- ACTION_BORING_1, ACTION_BORING_LAST);
- player->num_special_action_sleeping =
- get_num_special_action(player->artwork_element,
- ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
-
player->switch_x = -1;
player->switch_y = -1;
player->show_envelope = 0;
-#if 1
- SetPlayerMoveSpeed(player, level.initial_player_stepsize, TRUE);
-#else
- player->move_delay = game.initial_move_delay;
- player->move_delay_value = game.initial_move_delay_value;
-
- player->move_delay_value_next = -1;
-
- player->move_delay_reset_counter = 0;
-
- player->cannot_move = FALSE;
-#endif
+ SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
player->push_delay = -1; /* initialized when pushing starts */
player->push_delay_value = game.initial_push_delay_value;
player->drop_delay = 0;
+ player->drop_pressed_delay = 0;
- player->last_jx = player->last_jy = 0;
- player->jx = player->jy = 0;
+ player->last_jx = -1;
+ player->last_jy = -1;
+ player->jx = -1;
+ player->jy = -1;
player->shield_normal_time_left = 0;
player->shield_deadly_time_left = 0;
player->inventory_infinite_element = EL_UNDEFINED;
player->inventory_size = 0;
+ if (level.use_initial_inventory[i])
+ {
+ for (j = 0; j < level.initial_inventory_size[i]; j++)
+ {
+ int element = level.initial_inventory_content[i][j];
+ int collect_count = element_info[element].collect_count_initial;
+ int k;
+
+ if (!IS_CUSTOM_ELEMENT(element))
+ collect_count = 1;
+
+ if (collect_count == 0)
+ player->inventory_infinite_element = element;
+ else
+ for (k = 0; k < collect_count; k++)
+ if (player->inventory_size < MAX_INVENTORY_SIZE)
+ player->inventory_element[player->inventory_size++] = element;
+ }
+ }
+
DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
SnapField(player, 0, 0);
player->LevelSolved = FALSE;
player->GameOver = FALSE;
+
+ player->LevelSolved_GameWon = FALSE;
+ player->LevelSolved_GameEnd = FALSE;
+ player->LevelSolved_PanelOff = FALSE;
+ player->LevelSolved_SaveTape = FALSE;
+ player->LevelSolved_SaveScore = FALSE;
+ player->LevelSolved_CountingTime = 0;
+ player->LevelSolved_CountingScore = 0;
+
+ map_player_action[i] = i;
}
network_player_action_received = FALSE;
AllPlayersGone = FALSE;
+ game.no_time_limit = (level.time == 0);
+
game.yamyam_content_nr = 0;
+ game.robot_wheel_active = FALSE;
game.magic_wall_active = FALSE;
game.magic_wall_time_left = 0;
game.light_time_left = 0;
game.timegate_time_left = 0;
game.switchgate_pos = 0;
game.wind_direction = level.wind_direction_initial;
- game.gravity = level.initial_gravity;
- game.explosions_delayed = TRUE;
game.lenses_time_left = 0;
game.magnify_time_left = 0;
game.envelope_active = FALSE;
+ /* set focus to local player for network games, else to all players */
+ game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
+ game.centered_player_nr_next = game.centered_player_nr;
+ game.set_centered_player = FALSE;
+
+ if (network_playing && tape.recording)
+ {
+ /* store client dependent player focus when recording network games */
+ tape.centered_player_nr_next = game.centered_player_nr_next;
+ tape.set_centered_player = TRUE;
+ }
+
for (i = 0; i < NUM_BELTS; i++)
{
game.belt_dir[i] = MV_NONE;
for (i = 0; i < MAX_NUM_AMOEBA; i++)
AmoebaCnt[i] = AmoebaCnt2[i] = 0;
-#if 1
- SCAN_PLAYFIELD(x, y)
-#else
- for (x = 0; x < lev_fieldx; x++) for (y = 0; y < lev_fieldy; y++)
-#endif
+#if DEBUG_INIT_PLAYER
+ if (options.debug)
{
- Feld[x][y] = level.field[x][y];
- MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
+ printf("Player status at level initialization:\n");
+ }
+#endif
+
+ SCAN_PLAYFIELD(x, y)
+ {
+ Feld[x][y] = level.field[x][y];
+ MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
ChangeDelay[x][y] = 0;
ChangePage[x][y] = -1;
-#if USE_NEW_CUSTOM_VALUE
CustomValue[x][y] = 0; /* initialized in InitField() */
-#endif
Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
AmoebaNr[x][y] = 0;
WasJustMoving[x][y] = 0;
WasJustFalling[x][y] = 0;
CheckCollision[x][y] = 0;
+ CheckImpact[x][y] = 0;
Stop[x][y] = FALSE;
Pushed[x][y] = FALSE;
GfxElement[x][y] = EL_UNDEFINED;
GfxAction[x][y] = ACTION_DEFAULT;
GfxDir[x][y] = MV_NONE;
+ GfxRedraw[x][y] = GFX_REDRAW_NONE;
}
-#if 1
SCAN_PLAYFIELD(x, y)
-#else
- for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
-#endif
{
if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
emulate_bd = FALSE;
emulate_sp = FALSE;
InitField(x, y, TRUE);
+
+ ResetGfxAnimation(x, y);
}
InitBeltMovement();
+ for (i = 0; i < MAX_PLAYERS; i++)
+ {
+ struct PlayerInfo *player = &stored_player[i];
+
+ /* set number of special actions for bored and sleeping animation */
+ player->num_special_action_bored =
+ get_num_special_action(player->artwork_element,
+ ACTION_BORING_1, ACTION_BORING_LAST);
+ player->num_special_action_sleeping =
+ get_num_special_action(player->artwork_element,
+ ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
+ }
+
game.emulation = (emulate_bd ? EMU_BOULDERDASH :
emulate_sb ? EMU_SOKOBAN :
emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
-#if USE_NEW_ALL_SLIPPERY
/* initialize type of slippery elements */
for (i = 0; i < MAX_NUM_ELEMENTS; i++)
{
element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
}
}
-#endif
/* initialize explosion and ignition delay */
for (i = 0; i < MAX_NUM_ELEMENTS; i++)
if (i == EL_BLACK_ORB)
element_info[i].ignition_delay = 1;
}
-
-#if 0
- if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
- element_info[i].explosion_delay = 1;
-
- if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
- element_info[i].ignition_delay = 1;
-#endif
}
/* correct non-moving belts to start moving left */
if (game.belt_dir[i] == MV_NONE)
game.belt_dir_nr[i] = 3; /* not moving, next moving left */
+#if USE_NEW_PLAYER_ASSIGNMENTS
+ /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
+ /* choose default local player */
+ local_player = &stored_player[0];
+
+ for (i = 0; i < MAX_PLAYERS; i++)
+ stored_player[i].connected = FALSE;
+
+ local_player->connected = TRUE;
+ /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
+
+ if (tape.playing)
+ {
+ for (i = 0; i < MAX_PLAYERS; i++)
+ stored_player[i].connected = tape.player_participates[i];
+ }
+ else if (game.team_mode && !options.network)
+ {
+ /* try to guess locally connected team mode players (needed for correct
+ assignment of player figures from level to locally playing players) */
+
+ for (i = 0; i < MAX_PLAYERS; i++)
+ if (setup.input[i].use_joystick ||
+ setup.input[i].key.left != KSYM_UNDEFINED)
+ stored_player[i].connected = TRUE;
+ }
+
+#if DEBUG_INIT_PLAYER
+ if (options.debug)
+ {
+ printf("Player status after level initialization:\n");
+
+ for (i = 0; i < MAX_PLAYERS; i++)
+ {
+ struct PlayerInfo *player = &stored_player[i];
+
+ printf("- player %d: present == %d, connected == %d, active == %d",
+ i + 1,
+ player->present,
+ player->connected,
+ player->active);
+
+ if (local_player == player)
+ printf(" (local player)");
+
+ printf("\n");
+ }
+ }
+#endif
+
+#if DEBUG_INIT_PLAYER
+ if (options.debug)
+ printf("Reassigning players ...\n");
+#endif
+
+ /* check if any connected player was not found in playfield */
+ for (i = 0; i < MAX_PLAYERS; i++)
+ {
+ struct PlayerInfo *player = &stored_player[i];
+
+ if (player->connected && !player->present)
+ {
+ struct PlayerInfo *field_player = NULL;
+
+#if DEBUG_INIT_PLAYER
+ if (options.debug)
+ printf("- looking for field player for player %d ...\n", i + 1);
+#endif
+
+ /* assign first free player found that is present in the playfield */
+
+ /* first try: look for unmapped playfield player that is not connected */
+ for (j = 0; j < MAX_PLAYERS; j++)
+ if (field_player == NULL &&
+ stored_player[j].present &&
+ !stored_player[j].mapped &&
+ !stored_player[j].connected)
+ field_player = &stored_player[j];
+
+ /* second try: look for *any* unmapped playfield player */
+ for (j = 0; j < MAX_PLAYERS; j++)
+ if (field_player == NULL &&
+ stored_player[j].present &&
+ !stored_player[j].mapped)
+ field_player = &stored_player[j];
+
+ if (field_player != NULL)
+ {
+ int jx = field_player->jx, jy = field_player->jy;
+
+#if DEBUG_INIT_PLAYER
+ if (options.debug)
+ printf("- found player %d\n", field_player->index_nr + 1);
+#endif
+
+ player->present = FALSE;
+ player->active = FALSE;
+
+ field_player->present = TRUE;
+ field_player->active = TRUE;
+
+ /*
+ player->initial_element = field_player->initial_element;
+ player->artwork_element = field_player->artwork_element;
+
+ player->block_last_field = field_player->block_last_field;
+ player->block_delay_adjustment = field_player->block_delay_adjustment;
+ */
+
+ StorePlayer[jx][jy] = field_player->element_nr;
+
+ field_player->jx = field_player->last_jx = jx;
+ field_player->jy = field_player->last_jy = jy;
+
+ if (local_player == player)
+ local_player = field_player;
+
+ map_player_action[field_player->index_nr] = i;
+
+ field_player->mapped = TRUE;
+
+#if DEBUG_INIT_PLAYER
+ if (options.debug)
+ printf("- map_player_action[%d] == %d\n",
+ field_player->index_nr + 1, i + 1);
+#endif
+ }
+ }
+
+ if (player->connected && player->present)
+ player->mapped = TRUE;
+ }
+
+#if DEBUG_INIT_PLAYER
+ if (options.debug)
+ {
+ printf("Player status after player assignment (first stage):\n");
+
+ for (i = 0; i < MAX_PLAYERS; i++)
+ {
+ struct PlayerInfo *player = &stored_player[i];
+
+ printf("- player %d: present == %d, connected == %d, active == %d",
+ i + 1,
+ player->present,
+ player->connected,
+ player->active);
+
+ if (local_player == player)
+ printf(" (local player)");
+
+ printf("\n");
+ }
+ }
+#endif
+
+#else
+
/* check if any connected player was not found in playfield */
for (i = 0; i < MAX_PLAYERS; i++)
{
{
for (j = 0; j < MAX_PLAYERS; j++)
{
- struct PlayerInfo *some_player = &stored_player[j];
- int jx = some_player->jx, jy = some_player->jy;
+ struct PlayerInfo *field_player = &stored_player[j];
+ int jx = field_player->jx, jy = field_player->jy;
/* assign first free player found that is present in the playfield */
- if (some_player->present && !some_player->connected)
+ if (field_player->present && !field_player->connected)
{
player->present = TRUE;
player->active = TRUE;
- some_player->present = FALSE;
- some_player->active = FALSE;
-
-#if 0
- player->element_nr = some_player->element_nr;
-#endif
+ field_player->present = FALSE;
+ field_player->active = FALSE;
- player->artwork_element = some_player->artwork_element;
+ player->initial_element = field_player->initial_element;
+ player->artwork_element = field_player->artwork_element;
- player->block_last_field = some_player->block_last_field;
- player->block_delay_adjustment = some_player->block_delay_adjustment;
+ player->block_last_field = field_player->block_last_field;
+ player->block_delay_adjustment = field_player->block_delay_adjustment;
StorePlayer[jx][jy] = player->element_nr;
+
player->jx = player->last_jx = jx;
player->jy = player->last_jy = jy;
}
}
}
+#endif
+
+#if 0
+ printf("::: local_player->present == %d\n", local_player->present);
+#endif
if (tape.playing)
{
- /* when playing a tape, eliminate all players which do not participate */
+ /* when playing a tape, eliminate all players who do not participate */
+
+#if USE_NEW_PLAYER_ASSIGNMENTS
+
+ if (!game.team_mode)
+ {
+ for (i = 0; i < MAX_PLAYERS; i++)
+ {
+ if (stored_player[i].active &&
+ !tape.player_participates[map_player_action[i]])
+ {
+ struct PlayerInfo *player = &stored_player[i];
+ int jx = player->jx, jy = player->jy;
+
+#if DEBUG_INIT_PLAYER
+ if (options.debug)
+ printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
+#endif
+
+ player->active = FALSE;
+ StorePlayer[jx][jy] = 0;
+ Feld[jx][jy] = EL_EMPTY;
+ }
+ }
+ }
+
+#else
for (i = 0; i < MAX_PLAYERS; i++)
{
- if (stored_player[i].active && !tape.player_participates[i])
+ if (stored_player[i].active &&
+ !tape.player_participates[i])
{
struct PlayerInfo *player = &stored_player[i];
int jx = player->jx, jy = player->jy;
Feld[jx][jy] = EL_EMPTY;
}
}
+#endif
}
- else if (!options.network && !setup.team_mode) /* && !tape.playing */
+ else if (!options.network && !game.team_mode) /* && !tape.playing */
{
/* when in single player mode, eliminate all but the first active player */
/* when recording the game, store which players take part in the game */
if (tape.recording)
{
+#if USE_NEW_PLAYER_ASSIGNMENTS
+ for (i = 0; i < MAX_PLAYERS; i++)
+ if (stored_player[i].connected)
+ tape.player_participates[i] = TRUE;
+#else
for (i = 0; i < MAX_PLAYERS; i++)
if (stored_player[i].active)
tape.player_participates[i] = TRUE;
+#endif
}
+#if DEBUG_INIT_PLAYER
if (options.debug)
{
+ printf("Player status after player assignment (final stage):\n");
+
for (i = 0; i < MAX_PLAYERS; i++)
{
struct PlayerInfo *player = &stored_player[i];
- printf("Player %d: present == %d, connected == %d, active == %d.\n",
- i+1,
+ printf("- player %d: present == %d, connected == %d, active == %d",
+ i + 1,
player->present,
player->connected,
player->active);
+
if (local_player == player)
- printf("Player %d is local player.\n", i+1);
+ printf(" (local player)");
+
+ printf("\n");
}
}
+#endif
if (BorderElement == EL_EMPTY)
{
SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
}
- if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
+ if (full_lev_fieldx <= SCR_FIELDX)
SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
-
- if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
+ if (full_lev_fieldy <= SCR_FIELDY)
SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
+ if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
+ SBX_Left--;
+ if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
+ SBY_Upper--;
+
/* if local player not found, look for custom element that might create
the player (make some assumptions about the right custom element) */
if (!local_player->present)
int found_element = EL_UNDEFINED;
int player_nr = local_player->index_nr;
-#if 1
SCAN_PLAYFIELD(x, y)
-#else
- for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
-#endif
{
int element = Feld[x][y];
int content;
content = element_info[element].change_page[i].target_element;
is_player = ELEM_IS_PLAYER(content);
- if (is_player && (found_rating < 3 || element < found_element))
+ if (is_player && (found_rating < 3 ||
+ (found_rating == 3 && element < found_element)))
{
start_x = x;
start_y = y;
content = element_info[element].content.e[xx][yy];
is_player = ELEM_IS_PLAYER(content);
- if (is_player && (found_rating < 2 || element < found_element))
+ if (is_player && (found_rating < 2 ||
+ (found_rating == 2 && element < found_element)))
{
start_x = x + xx - 1;
start_y = y + yy - 1;
is_player = ELEM_IS_PLAYER(content);
- if (is_player && (found_rating < 1 || element < found_element))
+ if (is_player && (found_rating < 1 ||
+ (found_rating == 1 && element < found_element)))
{
start_x = x + xx - 1;
start_y = y + yy - 1;
local_player->jy - MIDPOSY);
}
- if (!game.restart_level)
- CloseDoor(DOOR_CLOSE_1);
-
/* !!! FIX THIS (START) !!! */
if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
{
InitGameEngine_EM();
}
+ else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+ {
+ InitGameEngine_SP();
+ }
else
{
- DrawLevel();
+ DrawLevel(REDRAW_FIELD);
DrawAllPlayers();
/* after drawing the level, correct some elements */
if (game.timegate_time_left == 0)
CloseAllOpenTimegates();
-
- if (setup.soft_scrolling)
- BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
-
- redraw_mask |= REDRAW_FROM_BACKBUFFER;
- FadeToFront();
}
+
+ /* blit playfield from scroll buffer to normal back buffer for fading in */
+ BlitScreenToBitmap(backbuffer);
/* !!! FIX THIS (END) !!! */
+ DrawMaskedBorder(fade_mask);
+
+ FadeIn(fade_mask);
+
+#if 1
+ // full screen redraw is required at this point in the following cases:
+ // - special editor door undrawn when game was started from level editor
+ // - drawing area (playfield) was changed and has to be removed completely
+ redraw_mask = REDRAW_ALL;
+ BackToFront();
+#endif
+
if (!game.restart_level)
{
/* copy default game door content to main double buffer */
- BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
- DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
+
+ /* !!! CHECK AGAIN !!! */
+ SetPanelBackground();
+ // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
+ DrawBackground(DX, DY, DXSIZE, DYSIZE);
}
- DrawGameDoorValues();
+ SetPanelBackground();
+ SetDrawBackgroundMask(REDRAW_DOOR_1);
+
+ UpdateAndDisplayGameControlValues();
if (!game.restart_level)
{
UnmapGameButtons();
UnmapTapeButtons();
+
+ FreeGameButtons();
+ CreateGameButtons();
+
game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
+
MapGameButtons();
MapTapeButtons();
/* copy actual game door content to door double buffer for OpenDoor() */
- BlitBitmap(drawto, bitmap_db_door,
- DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
+ BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
OpenDoor(DOOR_OPEN_ALL);
- PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
+ PlaySound(SND_GAME_STARTING);
if (setup.sound_music)
PlayLevelMusic();
KeyboardAutoRepeatOffUnlessAutoplay();
+#if DEBUG_INIT_PLAYER
if (options.debug)
{
+ printf("Player status (final):\n");
+
for (i = 0; i < MAX_PLAYERS; i++)
- printf("Player %d %sactive.\n",
- i + 1, (stored_player[i].active ? "" : "not "));
+ {
+ struct PlayerInfo *player = &stored_player[i];
+
+ printf("- player %d: present == %d, connected == %d, active == %d",
+ i + 1,
+ player->present,
+ player->connected,
+ player->active);
+
+ if (local_player == player)
+ printf(" (local player)");
+
+ printf("\n");
+ }
}
+#endif
+ }
+
+ UnmapAllGadgets();
+
+ MapGameButtons();
+ MapTapeButtons();
+
+ if (!game.restart_level && !tape.playing)
+ {
+ LevelStats_incPlayed(level_nr);
+
+ SaveLevelSetup_SeriesInfo();
}
game.restart_level = FALSE;
+
+ SaveEngineSnapshotToListInitial();
}
void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
{ MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
};
- switch(element)
+ switch (element)
{
case EL_BUG_RIGHT:
case EL_BUG_UP:
MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
break;
+ case EL_YAMYAM_LEFT:
+ case EL_YAMYAM_RIGHT:
+ case EL_YAMYAM_UP:
+ case EL_YAMYAM_DOWN:
+ Feld[x][y] = EL_YAMYAM;
+ MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
+ break;
+
case EL_SP_SNIKSNAK:
MovDir[x][y] = MV_UP;
break;
AmoebaCnt2[group_nr]++;
}
-void GameWon()
+static void PlayerWins(struct PlayerInfo *player)
{
- int hi_pos;
- boolean raise_level = FALSE;
-
- if (local_player->MovPos)
- return;
+ player->LevelSolved = TRUE;
+ player->GameOver = TRUE;
- if (tape.auto_play) /* tape might already be stopped here */
- tape.auto_play_level_solved = TRUE;
+ player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
+ level.native_em_level->lev->score : player->score);
- local_player->LevelSolved = FALSE;
+ player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
+ TimeLeft);
+ player->LevelSolved_CountingScore = player->score_final;
+}
- PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
+void GameWon()
+{
+ static int time, time_final;
+ static int score, score_final;
+ static int game_over_delay_1 = 0;
+ static int game_over_delay_2 = 0;
+ int game_over_delay_value_1 = 50;
+ int game_over_delay_value_2 = 50;
- if (TimeLeft)
+ if (!local_player->LevelSolved_GameWon)
{
- if (!tape.playing && setup.sound_loops)
- PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
- SND_CTRL_PLAY_LOOP);
+ int i;
+
+ /* do not start end game actions before the player stops moving (to exit) */
+ if (local_player->MovPos)
+ return;
+
+ local_player->LevelSolved_GameWon = TRUE;
+ local_player->LevelSolved_SaveTape = tape.recording;
+ local_player->LevelSolved_SaveScore = !tape.playing;
- while (TimeLeft > 0)
+ if (!tape.playing)
{
- if (!tape.playing && !setup.sound_loops)
- PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
+ LevelStats_incSolved(level_nr);
- if (TimeLeft > 100 && TimeLeft % 10 == 0)
- {
- TimeLeft -= 10;
- RaiseScore(level.score[SC_TIME_BONUS] * 10);
- }
- else
- {
- TimeLeft--;
- RaiseScore(level.score[SC_TIME_BONUS]);
- }
+ SaveLevelSetup_SeriesInfo();
+ }
- DrawGameValue_Time(TimeLeft);
+ if (tape.auto_play) /* tape might already be stopped here */
+ tape.auto_play_level_solved = TRUE;
- BackToFront();
+ TapeStop();
+
+ game_over_delay_1 = game_over_delay_value_1;
+ game_over_delay_2 = game_over_delay_value_2;
- if (!tape.playing)
- Delay(10);
+ time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
+ score = score_final = local_player->score_final;
+
+ if (TimeLeft > 0)
+ {
+ time_final = 0;
+ score_final += TimeLeft * level.score[SC_TIME_BONUS];
+ }
+ else if (game.no_time_limit && TimePlayed < 999)
+ {
+ time_final = 999;
+ score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
}
- if (!tape.playing && setup.sound_loops)
- StopSound(SND_GAME_LEVELTIME_BONUS);
- }
- else if (level.time == 0) /* level without time limit */
- {
- if (!tape.playing && setup.sound_loops)
- PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
- SND_CTRL_PLAY_LOOP);
+ local_player->score_final = score_final;
- while (TimePlayed < 999)
+ if (level_editor_test_game)
{
- if (!tape.playing && !setup.sound_loops)
- PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
+ time = time_final;
+ score = score_final;
- if (TimePlayed < 900 && TimePlayed % 10 == 0)
- {
- TimePlayed += 10;
- RaiseScore(level.score[SC_TIME_BONUS] * 10);
- }
- else
+ local_player->LevelSolved_CountingTime = time;
+ local_player->LevelSolved_CountingScore = score;
+
+ game_panel_controls[GAME_PANEL_TIME].value = time;
+ game_panel_controls[GAME_PANEL_SCORE].value = score;
+
+ DisplayGameControlValues();
+ }
+
+ if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
+ {
+ if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
{
- TimePlayed++;
- RaiseScore(level.score[SC_TIME_BONUS]);
+ /* close exit door after last player */
+ if ((AllPlayersGone &&
+ (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
+ Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
+ Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
+ Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
+ Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
+ {
+ int element = Feld[ExitX][ExitY];
+
+ Feld[ExitX][ExitY] =
+ (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
+ element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
+ element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
+ element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
+ EL_EM_STEEL_EXIT_CLOSING);
+
+ PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
+ }
+
+ /* player disappears */
+ DrawLevelField(ExitX, ExitY);
}
- DrawGameValue_Time(TimePlayed);
+ for (i = 0; i < MAX_PLAYERS; i++)
+ {
+ struct PlayerInfo *player = &stored_player[i];
- BackToFront();
+ if (player->present)
+ {
+ RemovePlayer(player);
- if (!tape.playing)
- Delay(10);
+ /* player disappears */
+ DrawLevelField(player->jx, player->jy);
+ }
+ }
}
- if (!tape.playing && setup.sound_loops)
- StopSound(SND_GAME_LEVELTIME_BONUS);
+ PlaySound(SND_GAME_WINNING);
}
- /* close exit door after last player */
- if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
- (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
- Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
+ if (game_over_delay_1 > 0)
{
- int element = Feld[ExitX][ExitY];
+ game_over_delay_1--;
- Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
- EL_SP_EXIT_CLOSING);
-
- PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
+ return;
}
- /* player disappears */
- if (ExitX >= 0 && ExitY >= 0)
- DrawLevelField(ExitX, ExitY);
+ if (time != time_final)
+ {
+ int time_to_go = ABS(time_final - time);
+ int time_count_dir = (time < time_final ? +1 : -1);
+ int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
- BackToFront();
+ time += time_count_steps * time_count_dir;
+ score += time_count_steps * level.score[SC_TIME_BONUS];
- if (tape.playing)
- return;
+ local_player->LevelSolved_CountingTime = time;
+ local_player->LevelSolved_CountingScore = score;
- CloseDoor(DOOR_CLOSE_1);
+ game_panel_controls[GAME_PANEL_TIME].value = time;
+ game_panel_controls[GAME_PANEL_SCORE].value = score;
- if (tape.recording)
- {
- TapeStop();
- SaveTape(tape.level_nr); /* Ask to save tape */
- }
+ DisplayGameControlValues();
- if (level_nr == leveldir_current->handicap_level)
- {
+ if (time == time_final)
+ StopSound(SND_GAME_LEVELTIME_BONUS);
+ else if (setup.sound_loops)
+ PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
+ else
+ PlaySound(SND_GAME_LEVELTIME_BONUS);
+
+ return;
+ }
+
+ local_player->LevelSolved_PanelOff = TRUE;
+
+ if (game_over_delay_2 > 0)
+ {
+ game_over_delay_2--;
+
+ return;
+ }
+
+ GameEnd();
+}
+
+void GameEnd()
+{
+ int hi_pos;
+ boolean raise_level = FALSE;
+
+ local_player->LevelSolved_GameEnd = TRUE;
+
+ if (!global.use_envelope_request)
+ CloseDoor(DOOR_CLOSE_1);
+
+ if (local_player->LevelSolved_SaveTape)
+ {
+ SaveTapeChecked(tape.level_nr); /* ask to save tape */
+ }
+
+ CloseDoor(DOOR_CLOSE_ALL);
+
+ if (level_editor_test_game)
+ {
+ game_status = GAME_MODE_MAIN;
+
+ DrawMainMenu();
+
+ return;
+ }
+
+ if (!local_player->LevelSolved_SaveScore)
+ {
+ game_status = GAME_MODE_MAIN;
+
+ DrawMainMenu();
+
+ return;
+ }
+
+ if (level_nr == leveldir_current->handicap_level)
+ {
leveldir_current->handicap_level++;
+
SaveLevelSetup_SeriesInfo();
}
- if (level_editor_test_game)
- local_player->score = -1; /* no highscore when playing from editor */
- else if (level_nr < leveldir_current->last_level)
- raise_level = TRUE; /* advance to next level */
+ if (level_nr < leveldir_current->last_level)
+ raise_level = TRUE; /* advance to next level */
if ((hi_pos = NewHiScore()) >= 0)
{
game_status = GAME_MODE_SCORES;
+
DrawHallOfFame(hi_pos);
+
if (raise_level)
{
level_nr++;
else
{
game_status = GAME_MODE_MAIN;
+
if (raise_level)
{
level_nr++;
TapeErase();
}
+
DrawMainMenu();
}
-
- BackToFront();
}
int NewHiScore()
LoadScore(level_nr);
- if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
- local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
+ if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
+ local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
return -1;
for (k = 0; k < MAX_SCORE_ENTRIES; k++)
{
- if (local_player->score > highscore[k].Score)
+ if (local_player->score_final > highscore[k].Score)
{
/* player has made it to the hall of fame */
#ifdef ONE_PER_NAME
for (l = k; l < MAX_SCORE_ENTRIES; l++)
- if (!strcmp(setup.player_name, highscore[l].Name))
+ 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
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;
+ highscore[k].Score = local_player->score_final;
position = k;
break;
}
return position;
}
-inline static int getElementMoveStepsize(int x, int y)
+inline static int getElementMoveStepsizeExt(int x, int y, int direction)
{
int element = Feld[x][y];
- int direction = MovDir[x][y];
int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
int horiz_move = (dx != 0);
/* special values for move stepsize for spring and things on conveyor belt */
if (horiz_move)
{
-#if 0
- if (element == EL_SPRING)
- step = sign * MOVE_STEPSIZE_NORMAL * 2;
- else if (CAN_FALL(element) && !CAN_MOVE(element) &&
- y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
- step = sign * MOVE_STEPSIZE_NORMAL / 2;
-#else
if (CAN_FALL(element) &&
y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
step = sign * MOVE_STEPSIZE_NORMAL / 2;
else if (element == EL_SPRING)
step = sign * MOVE_STEPSIZE_NORMAL * 2;
-#endif
}
return step;
}
+inline static int getElementMoveStepsize(int x, int y)
+{
+ return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
+}
+
void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
{
if (player->GfxAction != action || player->GfxDir != dir)
{
-#if 0
- printf("Player frame reset! (%d => %d, %d => %d)\n",
- player->GfxAction, action, player->GfxDir, dir);
-#endif
-
player->GfxAction = action;
player->GfxDir = dir;
player->Frame = 0;
}
}
-static void ResetRandomAnimationValue(int x, int y)
+static void ResetGfxFrame(int x, int y, boolean redraw)
{
- GfxRandom[x][y] = INIT_GFX_RANDOM();
+ int element = Feld[x][y];
+ int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
+ int last_gfx_frame = GfxFrame[x][y];
+
+ if (graphic_info[graphic].anim_global_sync)
+ GfxFrame[x][y] = FrameCounter;
+ else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
+ GfxFrame[x][y] = CustomValue[x][y];
+ else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
+ GfxFrame[x][y] = element_info[element].collect_score;
+ else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
+ GfxFrame[x][y] = ChangeDelay[x][y];
+
+ if (redraw && GfxFrame[x][y] != last_gfx_frame)
+ DrawLevelGraphicAnimation(x, y, graphic);
}
static void ResetGfxAnimation(int x, int y)
{
-#if 0
- int element, graphic;
-#endif
-
- GfxFrame[x][y] = 0;
GfxAction[x][y] = ACTION_DEFAULT;
GfxDir[x][y] = MovDir[x][y];
+ GfxFrame[x][y] = 0;
-#if 0
- element = Feld[x][y];
- graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
+ ResetGfxFrame(x, y, FALSE);
+}
- if (graphic_info[graphic].anim_global_sync)
- GfxFrame[x][y] = FrameCounter;
- else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
- GfxFrame[x][y] = CustomValue[x][y];
- else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
- GfxFrame[x][y] = element_info[element].collect_score;
-#endif
+static void ResetRandomAnimationValue(int x, int y)
+{
+ GfxRandom[x][y] = INIT_GFX_RANDOM();
}
void InitMovingField(int x, int y, int direction)
{
int element = Feld[x][y];
-#if 0
- int graphic;
-#endif
int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
int newx = x + dx;
int newy = y + dy;
+ boolean is_moving_before, is_moving_after;
+
+ /* check if element was/is moving or being moved before/after mode change */
+ is_moving_before = (WasJustMoving[x][y] != 0);
+ is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
- if (!WasJustMoving[x][y] || direction != MovDir[x][y])
+ /* reset animation only for moving elements which change direction of moving
+ or which just started or stopped moving
+ (else CEs with property "can move" / "not moving" are reset each frame) */
+ if (is_moving_before != is_moving_after ||
+ direction != MovDir[x][y])
ResetGfxAnimation(x, y);
MovDir[x][y] = direction;
GfxDir[x][y] = direction;
- GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
- ACTION_FALLING : ACTION_MOVING);
-
-#if 0
- graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
- if (graphic_info[graphic].anim_global_sync)
- GfxFrame[x][y] = FrameCounter;
- else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
- GfxFrame[x][y] = CustomValue[x][y];
- else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
- GfxFrame[x][y] = element_info[element].collect_score;
-#endif
+ GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
+ direction == MV_DOWN && CAN_FALL(element) ?
+ ACTION_FALLING : ACTION_MOVING);
/* this is needed for CEs with property "can move" / "not moving" */
- if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
+ if (is_moving_after)
{
if (Feld[newx][newy] == EL_EMPTY)
Feld[newx][newy] = EL_BLOCKED;
MovDir[newx][newy] = MovDir[x][y];
-#if USE_NEW_CUSTOM_VALUE
CustomValue[newx][newy] = CustomValue[x][y];
-#endif
GfxFrame[newx][newy] = GfxFrame[x][y];
GfxRandom[newx][newy] = GfxRandom[x][y];
void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
{
int direction = MovDir[x][y];
-#if 1
int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
-#else
- int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
- int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
-#endif
*goes_to_x = newx;
*goes_to_y = newy;
MovDir[x][y] = 0;
MovDelay[x][y] = 0;
-#if USE_NEW_CUSTOM_VALUE
CustomValue[x][y] = 0;
-#endif
AmoebaNr[x][y] = 0;
ChangeDelay[x][y] = 0;
ChangePage[x][y] = -1;
Pushed[x][y] = FALSE;
-#if 0
- ExplodeField[x][y] = EX_TYPE_NONE;
-#endif
-
GfxElement[x][y] = EL_UNDEFINED;
GfxAction[x][y] = ACTION_DEFAULT;
GfxDir[x][y] = MV_NONE;
Store[oldx][oldy] = Store2[oldx][oldy] = 0;
- DrawLevelField(oldx, oldy);
+ TEST_DrawLevelField(oldx, oldy);
return;
}
if (element == EL_BLOCKED &&
(Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
+ Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
+ Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
next_element = get_next_element(Feld[oldx][oldy]);
if (next_element != EL_UNDEFINED)
Feld[oldx][oldy] = next_element;
- DrawLevelField(oldx, oldy);
- DrawLevelField(newx, newy);
+ TEST_DrawLevelField(oldx, oldy);
+ TEST_DrawLevelField(newx, newy);
}
void DrawDynamite(int x, int y)
Bang(x, y);
}
-void DrawRelocatePlayer(struct PlayerInfo *player)
+static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
+{
+ boolean num_checked_players = 0;
+ int i;
+
+ for (i = 0; i < MAX_PLAYERS; i++)
+ {
+ if (stored_player[i].active)
+ {
+ int sx = stored_player[i].jx;
+ int sy = stored_player[i].jy;
+
+ if (num_checked_players == 0)
+ {
+ *sx1 = *sx2 = sx;
+ *sy1 = *sy2 = sy;
+ }
+ else
+ {
+ *sx1 = MIN(*sx1, sx);
+ *sy1 = MIN(*sy1, sy);
+ *sx2 = MAX(*sx2, sx);
+ *sy2 = MAX(*sy2, sy);
+ }
+
+ num_checked_players++;
+ }
+ }
+}
+
+static boolean checkIfAllPlayersFitToScreen_RND()
+{
+ int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
+
+ setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
+
+ return (sx2 - sx1 < SCR_FIELDX &&
+ sy2 - sy1 < SCR_FIELDY);
+}
+
+static void setScreenCenteredToAllPlayers(int *sx, int *sy)
+{
+ int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
+
+ setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
+
+ *sx = (sx1 + sx2) / 2;
+ *sy = (sy1 + sy2) / 2;
+}
+
+void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
+ boolean center_screen, boolean quick_relocation)
{
boolean ffwd_delay = (tape.playing && tape.fast_forward);
boolean no_delay = (tape.warp_forward);
int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
int wait_delay_value = (no_delay ? 0 : frame_delay_value);
- int jx = player->jx;
- int jy = player->jy;
+ int new_scroll_x, new_scroll_y;
- if (level.instant_relocation)
+ if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
{
- int offset = (setup.scroll_delay ? 3 : 0);
-
- if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
- {
- scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
- local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
- local_player->jx - MIDPOSX);
+ /* case 1: quick relocation inside visible screen (without scrolling) */
- scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
- local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
- local_player->jy - MIDPOSY);
- }
- else
- {
- if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
- (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
- scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
+ RedrawPlayfield();
- if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
- (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
- scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
+ return;
+ }
- /* don't scroll over playfield boundaries */
- if (scroll_x < SBX_Left || scroll_x > SBX_Right)
- scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
+ if (!level.shifted_relocation || center_screen)
+ {
+ /* relocation _with_ centering of screen */
- /* don't scroll over playfield boundaries */
- if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
- scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
- }
+ new_scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
+ x > SBX_Right + MIDPOSX ? SBX_Right :
+ x - MIDPOSX);
- RedrawPlayfield(TRUE, 0,0,0,0);
+ new_scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
+ y > SBY_Lower + MIDPOSY ? SBY_Lower :
+ y - MIDPOSY);
}
else
{
- int scroll_xx = -999, scroll_yy = -999;
+ /* relocation _without_ centering of screen */
- ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
+ int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
+ old_x > SBX_Right + MIDPOSX ? SBX_Right :
+ old_x - MIDPOSX);
- while (scroll_xx != scroll_x || scroll_yy != scroll_y)
- {
- int dx = 0, dy = 0;
- int fx = FX, fy = FY;
+ int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
+ old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
+ old_y - MIDPOSY);
- scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
- local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
- local_player->jx - MIDPOSX);
+ int offset_x = x + (scroll_x - center_scroll_x);
+ int offset_y = y + (scroll_y - center_scroll_y);
- scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
- local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
- local_player->jy - MIDPOSY);
+ new_scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
+ offset_x > SBX_Right + MIDPOSX ? SBX_Right :
+ offset_x - MIDPOSX);
- dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
- dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
+ new_scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
+ offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
+ offset_y - MIDPOSY);
+ }
- if (dx == 0 && dy == 0) /* no scrolling needed at all */
- break;
+ if (quick_relocation)
+ {
+ /* case 2: quick relocation (redraw without visible scrolling) */
- scroll_x -= dx;
- scroll_y -= dy;
+ scroll_x = new_scroll_x;
+ scroll_y = new_scroll_y;
- fx += dx * TILEX / 2;
- fy += dy * TILEY / 2;
+ RedrawPlayfield();
- ScrollLevel(dx, dy);
- DrawAllPlayers();
+ return;
+ }
- /* scroll in two steps of half tile size to make things smoother */
- BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
- FlushDisplay();
- Delay(wait_delay_value);
+ /* case 3: visible relocation (with scrolling to new position) */
- /* scroll second step to align at full tile size */
- BackToFront();
- Delay(wait_delay_value);
- }
+ ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
+
+ while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
+ {
+ int dx = 0, dy = 0;
+ int fx = FX, fy = FY;
+
+ dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
+ dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
+
+ if (dx == 0 && dy == 0) /* no scrolling needed at all */
+ break;
+
+ scroll_x -= dx;
+ scroll_y -= dy;
+
+ fx += dx * TILEX / 2;
+ fy += dy * TILEY / 2;
+
+ ScrollLevel(dx, dy);
+ DrawAllPlayers();
+
+ /* scroll in two steps of half tile size to make things smoother */
+ BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
+ Delay(wait_delay_value);
- DrawPlayer(player);
+ /* scroll second step to align at full tile size */
BackToFront();
Delay(wait_delay_value);
}
+
+ DrawAllPlayers();
+ BackToFront();
+ Delay(wait_delay_value);
}
void RelocatePlayer(int jx, int jy, int el_player_raw)
Feld[jx][jy] = el_player;
InitPlayerField(jx, jy, el_player, TRUE);
+ /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
+ possible that the relocation target field did not contain a player element,
+ but a walkable element, to which the new player was relocated -- in this
+ case, restore that (already initialized!) element on the player field */
if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
{
- Feld[jx][jy] = element;
- InitField(jx, jy, FALSE);
+ Feld[jx][jy] = element; /* restore previously existing element */
}
- if (player == local_player) /* only visually relocate local player */
- DrawRelocatePlayer(player);
+ /* only visually relocate centered player */
+ DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
+ FALSE, level.instant_relocation);
TestIfPlayerTouchesBadThing(jx, jy);
TestIfPlayerTouchesCustomElement(jx, jy);
CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
player->index_bit, enter_side);
+
+ if (player->is_switching)
+ {
+ /* ensure that relocation while still switching an element does not cause
+ a new element to be treated as also switched directly after relocation
+ (this is important for teleporter switches that teleport the player to
+ a place where another teleporter switch is in the same direction, which
+ would then incorrectly be treated as immediately switched before the
+ direction key that caused the switch was released) */
+
+ player->switch_x += jx - old_jx;
+ player->switch_y += jy - old_jy;
+ }
}
void Explode(int ex, int ey, int phase, int mode)
int center_element = Feld[ex][ey];
int artwork_element, explosion_element; /* set these values later */
-#if 0
- /* --- This is only really needed (and now handled) in "Impact()". --- */
- /* do not explode moving elements that left the explode field in time */
- if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
- center_element == EL_EMPTY &&
- (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
- return;
-#endif
-
-#if 0
- /* !!! at this place, the center element may be EL_BLOCKED !!! */
- if (mode == EX_TYPE_NORMAL ||
- mode == EX_TYPE_CENTER ||
- mode == EX_TYPE_CROSS)
- PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
-#endif
-
/* remove things displayed in background while burning dynamite */
if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
Back[ex][ey] = 0;
}
}
-#if 1
if (mode == EX_TYPE_NORMAL ||
mode == EX_TYPE_CENTER ||
mode == EX_TYPE_CROSS)
PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
-#endif
-#if 1
last_phase = element_info[explosion_element].explosion_delay + 1;
-#else
- last_phase = element_info[center_element].explosion_delay + 1;
-#endif
for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
{
if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
{
-#if 1
int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
-#else
- switch(StorePlayer[ex][ey])
- {
- case EL_PLAYER_2:
- Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
- break;
- case EL_PLAYER_3:
- Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
- break;
- case EL_PLAYER_4:
- Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
- break;
- case EL_PLAYER_1:
- default:
- Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
- break;
- }
-#endif
if (PLAYERINFO(ex, ey)->use_murphy)
Store[x][y] = EL_EMPTY;
}
-#if 1
+
/* !!! check this case -- currently needed for rnd_rado_negundo_v,
!!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
else if (ELEM_IS_PLAYER(center_element))
#endif
else
Store[x][y] = EL_EMPTY;
-#else
- else if (center_element == EL_MOLE)
- Store[x][y] = EL_EMERALD_RED;
- else if (center_element == EL_PENGUIN)
- Store[x][y] = EL_EMERALD_PURPLE;
- else if (center_element == EL_BUG)
- Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
- else if (center_element == EL_BD_BUTTERFLY)
- Store[x][y] = EL_BD_DIAMOND;
- else if (center_element == EL_SP_ELECTRON)
- Store[x][y] = EL_SP_INFOTRON;
- else if (center_element == EL_AMOEBA_TO_DIAMOND)
- Store[x][y] = level.amoeba_content;
- else if (center_element == EL_YAMYAM)
- Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
- else if (IS_CUSTOM_ELEMENT(center_element) &&
- element_info[center_element].content.e[xx][yy] != EL_EMPTY)
- Store[x][y] = element_info[center_element].content.e[xx][yy];
- else if (element == EL_WALL_EMERALD)
- Store[x][y] = EL_EMERALD;
- else if (element == EL_WALL_DIAMOND)
- Store[x][y] = EL_DIAMOND;
- else if (element == EL_WALL_BD_DIAMOND)
- Store[x][y] = EL_BD_DIAMOND;
- else if (element == EL_WALL_EMERALD_YELLOW)
- Store[x][y] = EL_EMERALD_YELLOW;
- else if (element == EL_WALL_EMERALD_RED)
- Store[x][y] = EL_EMERALD_RED;
- else if (element == EL_WALL_EMERALD_PURPLE)
- Store[x][y] = EL_EMERALD_PURPLE;
- else if (element == EL_WALL_PEARL)
- Store[x][y] = EL_PEARL;
- else if (element == EL_WALL_CRYSTAL)
- Store[x][y] = EL_CRYSTAL;
- else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
- Store[x][y] = element_info[element].content.e[1][1];
- else
- Store[x][y] = EL_EMPTY;
-#endif
if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
center_element == EL_AMOEBA_TO_DIAMOND)
ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
-#ifdef DEBUG
-
- /* activate this even in non-DEBUG version until cause for crash in
- getGraphicAnimationFrame() (see below) is found and eliminated */
-
-#endif
-#if 1
-
+ /* this can happen if the player leaves an explosion just in time */
if (GfxElement[x][y] == EL_UNDEFINED)
- {
- printf("\n\n");
- printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
- printf("Explode(): This should never happen!\n");
- printf("\n\n");
-
GfxElement[x][y] = EL_EMPTY;
- }
-#endif
border_element = Store2[x][y];
if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
ChangeDelay[x][y] = 0;
ChangePage[x][y] = -1;
-#if USE_NEW_CUSTOM_VALUE
CustomValue[x][y] = 0;
-#endif
InitField_WithBug2(x, y, FALSE);
- DrawLevelField(x, y);
+ TEST_DrawLevelField(x, y);
TestIfElementTouchesCustomElement(x, y);
if (GFX_CRUMBLED(element))
- DrawLevelFieldCrumbledSandNeighbours(x, y);
+ TEST_DrawLevelFieldCrumbledNeighbours(x, y);
if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
StorePlayer[x][y] = 0;
int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
if (phase == delay)
- DrawLevelFieldCrumbledSand(x, y);
+ TEST_DrawLevelFieldCrumbled(x, y);
if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
{
{
struct PlayerInfo *player = PLAYERINFO(x, y);
- element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
- player->element_nr);
+ element = Feld[x][y] = player->initial_element;
if (level.use_explosion_element[player->index_nr])
{
}
}
- switch(element)
+ switch (element)
{
case EL_BUG:
case EL_SPACESHIP:
explosion_type = EX_TYPE_DYNA;
break;
+ case EL_DC_LANDMINE:
+ explosion_type = EX_TYPE_CENTER;
+ break;
+
case EL_PENGUIN:
case EL_LAMP:
case EL_LAMP_ACTIVE:
for (j = 0; j < NUM_BELT_PARTS; j++)
{
int element = belt_base_active_element[belt_nr] + j;
- int graphic = el2img(element);
+ int graphic_1 = el2img(element);
+ int graphic_2 = el2panelimg(element);
if (game.belt_dir[i] == MV_LEFT)
- graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
+ {
+ graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
+ graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
+ }
else
- graphic_info[graphic].anim_mode |= ANIM_REVERSE;
+ {
+ graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
+ graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
+ }
}
}
-#if 1
SCAN_PLAYFIELD(x, y)
-#else
- for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
-#endif
{
int element = Feld[x][y];
for (i = 0; i < NUM_BELT_PARTS; i++)
{
int element = belt_base_active_element[belt_nr] + i;
- int graphic = el2img(element);
+ int graphic_1 = el2img(element);
+ int graphic_2 = el2panelimg(element);
if (belt_dir == MV_LEFT)
- graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
+ {
+ graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
+ graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
+ }
else
- graphic_info[graphic].anim_mode |= ANIM_REVERSE;
+ {
+ graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
+ graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
+ }
}
-#if 1
SCAN_PLAYFIELD(xx, yy)
-#else
- for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
-#endif
{
int element = Feld[xx][yy];
if (e_belt_nr == belt_nr)
{
Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
- DrawLevelField(xx, yy);
+ TEST_DrawLevelField(xx, yy);
}
}
else if (IS_BELT(element) && belt_dir != MV_NONE)
int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
- DrawLevelField(xx, yy);
+ TEST_DrawLevelField(xx, yy);
}
}
else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
- DrawLevelField(xx, yy);
+ TEST_DrawLevelField(xx, yy);
}
}
}
game.switchgate_pos = !game.switchgate_pos;
-#if 1
SCAN_PLAYFIELD(xx, yy)
-#else
- for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
-#endif
{
int element = Feld[xx][yy];
- if (element == EL_SWITCHGATE_SWITCH_UP ||
- element == EL_SWITCHGATE_SWITCH_DOWN)
+ if (element == EL_SWITCHGATE_SWITCH_UP)
{
- Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
- DrawLevelField(xx, yy);
+ Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
+ TEST_DrawLevelField(xx, yy);
}
- else if (element == EL_SWITCHGATE_OPEN ||
- element == EL_SWITCHGATE_OPENING)
+ else if (element == EL_SWITCHGATE_SWITCH_DOWN)
+ {
+ Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
+ TEST_DrawLevelField(xx, yy);
+ }
+ else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
+ {
+ Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
+ TEST_DrawLevelField(xx, yy);
+ }
+ else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
+ {
+ Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
+ TEST_DrawLevelField(xx, yy);
+ }
+ else if (element == EL_SWITCHGATE_OPEN ||
+ element == EL_SWITCHGATE_OPENING)
{
Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
{
int x, y;
-#if 1
SCAN_PLAYFIELD(x, y)
-#else
- for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
-#endif
{
int element = Feld[x][y];
game.light_time_left > 0)
{
Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
- DrawLevelField(x, y);
+ TEST_DrawLevelField(x, y);
}
else if (element == EL_LIGHT_SWITCH_ACTIVE &&
game.light_time_left == 0)
{
Feld[x][y] = EL_LIGHT_SWITCH;
- DrawLevelField(x, y);
+ TEST_DrawLevelField(x, y);
}
else if (element == EL_EMC_DRIPPER &&
game.light_time_left > 0)
{
Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
- DrawLevelField(x, y);
+ TEST_DrawLevelField(x, y);
}
else if (element == EL_EMC_DRIPPER_ACTIVE &&
game.light_time_left == 0)
{
Feld[x][y] = EL_EMC_DRIPPER;
- DrawLevelField(x, y);
+ TEST_DrawLevelField(x, y);
}
else if (element == EL_INVISIBLE_STEELWALL ||
element == EL_INVISIBLE_WALL ||
if (game.light_time_left > 0)
Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
- DrawLevelField(x, y);
+ TEST_DrawLevelField(x, y);
/* uncrumble neighbour fields, if needed */
if (element == EL_INVISIBLE_SAND)
- DrawLevelFieldCrumbledSandNeighbours(x, y);
+ TEST_DrawLevelFieldCrumbledNeighbours(x, y);
}
else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
element == EL_INVISIBLE_WALL_ACTIVE ||
if (game.light_time_left == 0)
Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
- DrawLevelField(x, y);
+ TEST_DrawLevelField(x, y);
/* re-crumble neighbour fields, if needed */
if (element == EL_INVISIBLE_SAND)
- DrawLevelFieldCrumbledSandNeighbours(x, y);
+ TEST_DrawLevelFieldCrumbledNeighbours(x, y);
}
}
}
{
int x, y;
-#if 1
SCAN_PLAYFIELD(x, y)
-#else
- for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
-#endif
{
int element = Feld[x][y];
game.lenses_time_left > 0)
{
Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
- DrawLevelField(x, y);
+ TEST_DrawLevelField(x, y);
}
else if (element == EL_EMC_DRIPPER_ACTIVE &&
game.lenses_time_left == 0)
{
Feld[x][y] = EL_EMC_DRIPPER;
- DrawLevelField(x, y);
+ TEST_DrawLevelField(x, y);
}
else if (element == EL_INVISIBLE_STEELWALL ||
element == EL_INVISIBLE_WALL ||
if (game.lenses_time_left > 0)
Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
- DrawLevelField(x, y);
+ TEST_DrawLevelField(x, y);
/* uncrumble neighbour fields, if needed */
if (element == EL_INVISIBLE_SAND)
- DrawLevelFieldCrumbledSandNeighbours(x, y);
+ TEST_DrawLevelFieldCrumbledNeighbours(x, y);
}
else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
element == EL_INVISIBLE_WALL_ACTIVE ||
if (game.lenses_time_left == 0)
Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
- DrawLevelField(x, y);
+ TEST_DrawLevelField(x, y);
/* re-crumble neighbour fields, if needed */
if (element == EL_INVISIBLE_SAND)
- DrawLevelFieldCrumbledSandNeighbours(x, y);
+ TEST_DrawLevelFieldCrumbledNeighbours(x, y);
}
}
}
{
int x, y;
-#if 1
SCAN_PLAYFIELD(x, y)
-#else
- for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
-#endif
{
int element = Feld[x][y];
game.magnify_time_left > 0)
{
Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
- DrawLevelField(x, y);
+ TEST_DrawLevelField(x, y);
}
else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
game.magnify_time_left == 0)
{
Feld[x][y] = EL_EMC_FAKE_GRASS;
- DrawLevelField(x, y);
+ TEST_DrawLevelField(x, y);
}
else if (IS_GATE_GRAY(element) &&
game.magnify_time_left > 0)
element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
IS_EMC_GATE_GRAY(element) ?
element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
+ IS_DC_GATE_GRAY(element) ?
+ EL_DC_GATE_WHITE_GRAY_ACTIVE :
element);
- DrawLevelField(x, y);
+ TEST_DrawLevelField(x, y);
}
else if (IS_GATE_GRAY_ACTIVE(element) &&
game.magnify_time_left == 0)
element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
IS_EMC_GATE_GRAY_ACTIVE(element) ?
element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
+ IS_DC_GATE_GRAY_ACTIVE(element) ?
+ EL_DC_GATE_WHITE_GRAY :
element);
- DrawLevelField(x, y);
+ TEST_DrawLevelField(x, y);
}
}
}
game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
-#if 1
SCAN_PLAYFIELD(xx, yy)
-#else
- for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
-#endif
{
int element = Feld[xx][yy];
element == EL_TIMEGATE_CLOSING)
{
Feld[xx][yy] = EL_TIMEGATE_OPENING;
- PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
+ PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
}
/*
else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
{
Feld[xx][yy] = EL_TIMEGATE_SWITCH;
- DrawLevelField(xx, yy);
+ TEST_DrawLevelField(xx, yy);
}
*/
}
- Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
+ Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
+ EL_DC_TIMEGATE_SWITCH_ACTIVE);
}
void Impact(int x, int y)
RemoveMovingField(x, y + 1);
Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
Feld[x][y + 2] = EL_ROCK;
- DrawLevelField(x, y + 2);
+ TEST_DrawLevelField(x, y + 2);
+
+ object_hit = TRUE;
+ }
+
+ if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
+ {
+ RemoveMovingField(x, y + 1);
+ Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
+ Feld[x][y + 2] = EL_ROCK;
+ TEST_DrawLevelField(x, y + 2);
object_hit = TRUE;
}
el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
{
ResetGfxAnimation(x, y);
- DrawLevelField(x, y);
+ TEST_DrawLevelField(x, y);
}
if (impact && CAN_EXPLODE_IMPACT(element))
Bang(x, y);
return;
}
- else if (impact && element == EL_PEARL)
+ else if (impact && element == EL_PEARL &&
+ smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
{
ResetGfxAnimation(x, y);
if (object_hit) /* check which object was hit */
{
- if (CAN_PASS_MAGIC_WALL(element) &&
- (smashed == EL_MAGIC_WALL ||
- smashed == EL_BD_MAGIC_WALL))
+ if ((CAN_PASS_MAGIC_WALL(element) &&
+ (smashed == EL_MAGIC_WALL ||
+ smashed == EL_BD_MAGIC_WALL)) ||
+ (CAN_PASS_DC_MAGIC_WALL(element) &&
+ smashed == EL_DC_MAGIC_WALL))
{
int xx, yy;
int activated_magic_wall =
(smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
- EL_BD_MAGIC_WALL_ACTIVE);
+ smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
+ EL_DC_MAGIC_WALL_ACTIVE);
/* activate magic wall / mill */
-#if 1
SCAN_PLAYFIELD(xx, yy)
-#else
- for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
-#endif
+ {
if (Feld[xx][yy] == smashed)
Feld[xx][yy] = activated_magic_wall;
+ }
game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
game.magic_wall_active = TRUE;
PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
SND_MAGIC_WALL_ACTIVATING :
- SND_BD_MAGIC_WALL_ACTIVATING));
+ smashed == EL_BD_MAGIC_WALL ?
+ SND_BD_MAGIC_WALL_ACTIVATING :
+ SND_DC_MAGIC_WALL_ACTIVATING));
}
if (IS_PLAYER(x, y + 1))
ToggleBeltSwitch(x, y + 1);
}
else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
- smashed == EL_SWITCHGATE_SWITCH_DOWN)
+ smashed == EL_SWITCHGATE_SWITCH_DOWN ||
+ smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
+ smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
{
ToggleSwitchgateSwitch(x, y + 1);
}
}
else
{
-#if 0
- TestIfElementSmashesCustomElement(x, y, MV_DOWN);
-#endif
-
CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
CheckElementChangeBySide(x, y + 1, smashed, element,
/* play sound of magic wall / mill */
if (!last_line &&
(Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
- Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
+ Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
+ Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
{
if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
+ else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
+ PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
return;
}
}
else if (element == EL_SPRING)
{
-#if USE_NEW_SPRING_BUMPER
if (MovDir[x][y] & MV_HORIZONTAL)
{
if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
{
Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
ResetGfxAnimation(move_x, move_y);
- DrawLevelField(move_x, move_y);
+ TEST_DrawLevelField(move_x, move_y);
MovDir[x][y] = back_dir;
}
SPRING_CAN_ENTER_FIELD(element, x, y + 1))
MovDir[x][y] = MV_NONE;
}
-#else
- if (MovDir[x][y] & MV_HORIZONTAL &&
- (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
- SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
- MovDir[x][y] = MV_NONE;
-#endif
MovDelay[x][y] = 0;
}
int ex = x + xy[i][0];
int ey = y + xy[i][1];
- if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
+ if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
+ Feld[ex][ey] == EL_EM_EXIT_OPEN ||
+ Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
+ Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
{
attr_x = ex;
attr_y = ey;
static void TurnRound(int x, int y)
{
int direction = MovDir[x][y];
-#if 1
- int element, graphic;
-#endif
TurnRoundExt(x, y);
if (MovDelay[x][y])
GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
-#if 1
- element = Feld[x][y];
- graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
-
- if (graphic_info[graphic].anim_global_sync)
- GfxFrame[x][y] = FrameCounter;
- else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
- GfxFrame[x][y] = CustomValue[x][y];
- else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
- GfxFrame[x][y] = element_info[element].collect_score;
-#endif
+ ResetGfxFrame(x, y, FALSE);
}
static boolean JustBeingPushed(int x, int y)
started_moving = TRUE;
Feld[x][y] = EL_QUICKSAND_EMPTYING;
+#if USE_QUICKSAND_BD_ROCK_BUGFIX
+ if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
+ Store[x][y] = EL_ROCK;
+#else
Store[x][y] = EL_ROCK;
+#endif
PlayLevelSoundAction(x, y, ACTION_EMPTYING);
}
else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
{
if (!MovDelay[x][y])
+ {
+ MovDelay[x][y] = TILEY + 1;
+
+ ResetGfxAnimation(x, y);
+ ResetGfxAnimation(x, y + 1);
+ }
+
+ if (MovDelay[x][y])
+ {
+ DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
+ DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
+
+ MovDelay[x][y]--;
+ if (MovDelay[x][y])
+ return;
+ }
+
+ Feld[x][y] = EL_QUICKSAND_EMPTY;
+ Feld[x][y + 1] = EL_QUICKSAND_FULL;
+ Store[x][y + 1] = Store[x][y];
+ Store[x][y] = 0;
+
+ PlayLevelSoundAction(x, y, ACTION_FILLING);
+ }
+ else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
+ {
+ if (!MovDelay[x][y])
+ {
MovDelay[x][y] = TILEY + 1;
+ ResetGfxAnimation(x, y);
+ ResetGfxAnimation(x, y + 1);
+ }
+
if (MovDelay[x][y])
{
+ DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
+ DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
+
MovDelay[x][y]--;
if (MovDelay[x][y])
return;
}
Feld[x][y] = EL_QUICKSAND_EMPTY;
+ Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
+ Store[x][y + 1] = Store[x][y];
+ Store[x][y] = 0;
+
+ PlayLevelSoundAction(x, y, ACTION_FILLING);
+ }
+ }
+ else if (element == EL_QUICKSAND_FAST_FULL)
+ {
+ if (IS_FREE(x, y + 1))
+ {
+ InitMovingField(x, y, MV_DOWN);
+ started_moving = TRUE;
+
+ Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
+#if USE_QUICKSAND_BD_ROCK_BUGFIX
+ if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
+ Store[x][y] = EL_ROCK;
+#else
+ Store[x][y] = EL_ROCK;
+#endif
+
+ PlayLevelSoundAction(x, y, ACTION_EMPTYING);
+ }
+ else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
+ {
+ if (!MovDelay[x][y])
+ {
+ MovDelay[x][y] = TILEY + 1;
+
+ ResetGfxAnimation(x, y);
+ ResetGfxAnimation(x, y + 1);
+ }
+
+ if (MovDelay[x][y])
+ {
+ DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
+ DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
+
+ MovDelay[x][y]--;
+ if (MovDelay[x][y])
+ return;
+ }
+
+ Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
+ Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
+ Store[x][y + 1] = Store[x][y];
+ Store[x][y] = 0;
+
+ PlayLevelSoundAction(x, y, ACTION_FILLING);
+ }
+ else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
+ {
+ if (!MovDelay[x][y])
+ {
+ MovDelay[x][y] = TILEY + 1;
+
+ ResetGfxAnimation(x, y);
+ ResetGfxAnimation(x, y + 1);
+ }
+
+ if (MovDelay[x][y])
+ {
+ DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
+ DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
+
+ MovDelay[x][y]--;
+ if (MovDelay[x][y])
+ return;
+ }
+
+ Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
Feld[x][y + 1] = EL_QUICKSAND_FULL;
Store[x][y + 1] = Store[x][y];
Store[x][y] = 0;
PlayLevelSoundAction(x, y, ACTION_FILLING);
}
+ else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
+ Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
+ {
+ InitMovingField(x, y, MV_DOWN);
+ started_moving = TRUE;
+
+ Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
+ Store[x][y] = element;
+
+ PlayLevelSoundAction(x, y, ACTION_FILLING);
+ }
else if (element == EL_MAGIC_WALL_FULL)
{
if (IS_FREE(x, y + 1))
else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
{
if (!MovDelay[x][y])
- MovDelay[x][y] = TILEY/4 + 1;
+ MovDelay[x][y] = TILEY / 4 + 1;
if (MovDelay[x][y])
{
started_moving = TRUE;
Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
- Store[x][y] = EL_CHANGED2(Store[x][y]);
+ Store[x][y] = EL_CHANGED_BD(Store[x][y]);
}
else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
{
if (!MovDelay[x][y])
- MovDelay[x][y] = TILEY/4 + 1;
+ MovDelay[x][y] = TILEY / 4 + 1;
if (MovDelay[x][y])
{
Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
- Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
+ Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
+ Store[x][y] = 0;
+ }
+ }
+ else if (element == EL_DC_MAGIC_WALL_FULL)
+ {
+ if (IS_FREE(x, y + 1))
+ {
+ InitMovingField(x, y, MV_DOWN);
+ started_moving = TRUE;
+
+ Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
+ Store[x][y] = EL_CHANGED_DC(Store[x][y]);
+ }
+ else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
+ {
+ if (!MovDelay[x][y])
+ MovDelay[x][y] = TILEY / 4 + 1;
+
+ if (MovDelay[x][y])
+ {
+ MovDelay[x][y]--;
+ if (MovDelay[x][y])
+ return;
+ }
+
+ Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
+ Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
+ Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
Store[x][y] = 0;
}
}
- else if (CAN_PASS_MAGIC_WALL(element) &&
- (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
- Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
+ else if ((CAN_PASS_MAGIC_WALL(element) &&
+ (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
+ Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
+ (CAN_PASS_DC_MAGIC_WALL(element) &&
+ (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
+
{
InitMovingField(x, y, MV_DOWN);
started_moving = TRUE;
Feld[x][y] =
(Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
- EL_BD_MAGIC_WALL_FILLING);
+ Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
+ EL_DC_MAGIC_WALL_FILLING);
Store[x][y] = element;
}
else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
Store[x][y] = EL_ACID;
}
- else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
- CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
-
+ else if (
+ (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
+ CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
(game.engine_version >= VERSION_IDENT(3,0,7,0) &&
CAN_FALL(element) && WasJustFalling[x][y] &&
(Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
simply not covered here... :-/ ) */
CheckCollision[x][y] = 0;
+ CheckImpact[x][y] = 0;
Impact(x, y);
}
boolean can_fall_both = (can_fall_left && can_fall_right);
int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
-#if USE_NEW_ALL_SLIPPERY
if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
{
if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
can_fall_any = (can_fall_left || can_fall_right);
can_fall_both = FALSE;
}
-#else
- if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
- {
- if (slippery_type == SLIPPERY_ONLY_LEFT)
- can_fall_right = FALSE;
- else if (slippery_type == SLIPPERY_ONLY_RIGHT)
- can_fall_left = FALSE;
- else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
- can_fall_right = FALSE;
- else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
- can_fall_left = FALSE;
-
- can_fall_any = (can_fall_left || can_fall_right);
- can_fall_both = (can_fall_left && can_fall_right);
- }
-#endif
-
-#if USE_NEW_ALL_SLIPPERY
-#else
-#if USE_NEW_SP_SLIPPERY
- /* !!! better use the same properties as for custom elements here !!! */
- else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
- can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
- {
- can_fall_right = FALSE; /* slip down on left side */
- can_fall_both = FALSE;
- }
-#endif
-#endif
-#if USE_NEW_ALL_SLIPPERY
if (can_fall_both)
{
if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
can_fall_both = FALSE;
}
-#else
- if (can_fall_both)
- {
- if (game.emulation == EMU_BOULDERDASH ||
- element == EL_BD_ROCK || element == EL_BD_DIAMOND)
- can_fall_right = FALSE; /* slip down on left side */
- else
- can_fall_left = !(can_fall_right = RND(2));
-
- can_fall_both = FALSE;
- }
-#endif
if (can_fall_any)
{
started_moving = TRUE;
}
}
-#if 0
- else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
-#else
else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
-#endif
{
boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
}
/* not "else if" because of elements that can fall and move (EL_SPRING) */
-#if 0
- if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
-#else
if (CAN_MOVE(element) && !started_moving)
-#endif
{
int move_pattern = element_info[element].move_pattern;
int newx, newy;
-#if 0
-#if DEBUG
- if (MovDir[x][y] == MV_NONE)
- {
- printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
- x, y, element, element_info[element].token_name);
- printf("StartMoving(): This should never happen!\n");
- }
-#endif
-#endif
-
Moving2Blocked(x, y, &newx, &newy);
if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
element == EL_SP_SNIKSNAK ||
element == EL_SP_ELECTRON ||
element == EL_MOLE))
- DrawLevelField(x, y);
+ TEST_DrawLevelField(x, y);
}
}
if (IS_PLAYER(x, y))
DrawPlayerField(x, y);
else
- DrawLevelField(x, y);
+ TEST_DrawLevelField(x, y);
PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
{
int flamed = MovingOrBlocked2Element(xx, yy);
- /* !!! */
-#if 0
- if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
- Bang(xx, yy);
- else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
- RemoveMovingField(xx, yy);
- else
- RemoveField(xx, yy);
-#else
if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
Bang(xx, yy);
else
RemoveMovingField(xx, yy);
-#endif
ChangeDelay[xx][yy] = 0;
if (IN_SCR_FIELD(sx, sy))
{
- DrawLevelFieldCrumbledSand(xx, yy);
+ TEST_DrawLevelFieldCrumbled(xx, yy);
DrawGraphic(sx, sy, flame_graphic, frame);
}
}
{
if (Feld[xx][yy] == EL_FLAMES)
Feld[xx][yy] = EL_EMPTY;
- DrawLevelField(xx, yy);
+ TEST_DrawLevelField(xx, yy);
}
}
}
}
else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
{
- if (Feld[newx][newy] == EL_EXIT_OPEN)
+ if (Feld[newx][newy] == EL_EXIT_OPEN ||
+ Feld[newx][newy] == EL_EM_EXIT_OPEN ||
+ Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
+ Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
{
RemoveField(x, y);
- DrawLevelField(x, y);
+ TEST_DrawLevelField(x, y);
PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
local_player->friends_still_needed--;
if (!local_player->friends_still_needed &&
!local_player->GameOver && AllPlayersGone)
- local_player->LevelSolved = local_player->GameOver = TRUE;
+ PlayerWins(local_player);
return;
}
else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
{
if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
- DrawLevelField(newx, newy);
+ TEST_DrawLevelField(newx, newy);
else
GfxDir[x][y] = MovDir[x][y] = MV_NONE;
}
if (IS_PLAYER(x, y))
DrawPlayerField(x, y);
else
- DrawLevelField(x, y);
+ TEST_DrawLevelField(x, y);
return;
}
else
{
Feld[newx][newy] = EL_EMPTY;
- DrawLevelField(newx, newy);
+ TEST_DrawLevelField(newx, newy);
}
PlayLevelSound(x, y, SND_PIG_DIGGING);
if (IS_PLAYER(x, y))
DrawPlayerField(x, y);
else
- DrawLevelField(x, y);
+ TEST_DrawLevelField(x, y);
return;
}
else
{
Feld[newx][newy] = EL_EMPTY;
- DrawLevelField(newx, newy);
+ TEST_DrawLevelField(newx, newy);
PlayLevelSoundAction(x, y, ACTION_DIGGING);
}
}
else if (!IS_FREE(newx, newy))
{
-#if 0
- if (IS_PLAYER(x, y))
- DrawPlayerField(x, y);
- else
- DrawLevelField(x, y);
-#endif
-
return;
}
}
else if (IS_CUSTOM_ELEMENT(element) &&
CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
{
- int new_element = Feld[newx][newy];
-
- if (!IS_FREE(newx, newy))
- {
- int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
- IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
- ACTION_BREAKING);
-
- /* no element can dig solid indestructible elements */
- if (IS_INDESTRUCTIBLE(new_element) &&
- !IS_DIGGABLE(new_element) &&
- !IS_COLLECTIBLE(new_element))
- return;
-
- if (AmoebaNr[newx][newy] &&
- (new_element == EL_AMOEBA_FULL ||
- new_element == EL_BD_AMOEBA ||
- new_element == EL_AMOEBA_GROWING))
- {
- AmoebaCnt[AmoebaNr[newx][newy]]--;
- AmoebaCnt2[AmoebaNr[newx][newy]]--;
- }
-
- if (IS_MOVING(newx, newy))
- RemoveMovingField(newx, newy);
- else
- {
- RemoveField(newx, newy);
- DrawLevelField(newx, newy);
- }
-
- /* if digged element was about to explode, prevent the explosion */
- ExplodeField[newx][newy] = EX_TYPE_NONE;
-
- PlayLevelSoundAction(x, y, action);
- }
-
- Store[newx][newy] = EL_EMPTY;
-#if 1
- /* this makes it possible to leave the removed element again */
- if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
- Store[newx][newy] = new_element;
-#else
- if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
- {
- int move_leave_element = element_info[element].move_leave_element;
-
- /* this makes it possible to leave the removed element again */
- Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
- new_element : move_leave_element);
- }
-#endif
+ if (!DigFieldByCE(newx, newy, element))
+ return;
if (move_pattern & MV_MAZE_RUNNER_STYLE)
{
if (IS_PLAYER(x, y))
DrawPlayerField(x, y);
else
- DrawLevelField(x, y);
+ TEST_DrawLevelField(x, y);
return;
}
if (IS_PLAYER(x, y))
DrawPlayerField(x, y);
else
- DrawLevelField(x, y);
+ TEST_DrawLevelField(x, y);
PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
MovDelay[x][y] = 50;
- /* !!! */
-#if 0
- RemoveField(newx, newy);
-#endif
Feld[newx][newy] = EL_FLAMES;
if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
- {
-#if 0
- RemoveField(newx1, newy1);
-#endif
Feld[newx1][newy1] = EL_FLAMES;
- }
if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
- {
-#if 0
- RemoveField(newx2, newy2);
-#endif
Feld[newx2][newy2] = EL_FLAMES;
- }
return;
}
else
{
Feld[newx][newy] = EL_EMPTY;
- DrawLevelField(newx, newy);
+ TEST_DrawLevelField(newx, newy);
}
PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
AmoebaCnt[AmoebaNr[newx][newy]]--;
}
-#if 0
- /* !!! test !!! */
- if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
- {
- RemoveMovingField(newx, newy);
- }
-#else
if (IS_MOVING(newx, newy))
{
RemoveMovingField(newx, newy);
}
-#endif
else
{
Feld[newx][newy] = EL_EMPTY;
- DrawLevelField(newx, newy);
+ TEST_DrawLevelField(newx, newy);
}
PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
ResetGfxAnimation(x, y);
GfxAction[x][y] = ACTION_DIGGING;
- DrawLevelField(x, y);
+ TEST_DrawLevelField(x, y);
MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
else /* element == EL_PACMAN */
{
Feld[newx][newy] = EL_EMPTY;
- DrawLevelField(newx, newy);
+ TEST_DrawLevelField(newx, newy);
PlayLevelSound(x, y, SND_PACMAN_DIGGING);
}
}
TurnRound(x, y);
-#if 0
- /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
- if (move_pattern & MV_ANY_DIRECTION &&
- move_pattern == MovDir[x][y])
- {
- int blocking_element =
- (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
-
- CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
- MovDir[x][y]);
-
- element = Feld[x][y]; /* element might have changed */
- }
-#endif
-
if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
DrawLevelElementAnimation(x, y, element);
if (ABS(MovPos[x][y]) < TILEX)
{
- DrawLevelField(x, y);
+ TEST_DrawLevelField(x, y);
return; /* element is still moving */
}
{
Feld[x][y] = EL_SAND;
- DrawLevelFieldCrumbledSandNeighbours(x, y);
+ TEST_DrawLevelFieldCrumbledNeighbours(x, y);
}
else if (element == EL_QUICKSAND_FILLING)
{
Feld[x][y] = get_next_element(element);
element = Feld[newx][newy] = Store[x][y];
}
+ else if (element == EL_QUICKSAND_FAST_FILLING)
+ {
+ element = Feld[newx][newy] = get_next_element(element);
+ Store[newx][newy] = Store[x][y];
+ }
+ else if (element == EL_QUICKSAND_FAST_EMPTYING)
+ {
+ Feld[x][y] = get_next_element(element);
+ element = Feld[newx][newy] = Store[x][y];
+ }
else if (element == EL_MAGIC_WALL_FILLING)
{
element = Feld[newx][newy] = get_next_element(element);
Feld[x][y] = EL_MAGIC_WALL_DEAD;
element = Feld[newx][newy] = Store[x][y];
-#if USE_NEW_CUSTOM_VALUE
InitField(newx, newy, FALSE);
-#endif
}
else if (element == EL_BD_MAGIC_WALL_FILLING)
{
Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
element = Feld[newx][newy] = Store[x][y];
-#if USE_NEW_CUSTOM_VALUE
InitField(newx, newy, FALSE);
-#endif
+ }
+ else if (element == EL_DC_MAGIC_WALL_FILLING)
+ {
+ element = Feld[newx][newy] = get_next_element(element);
+ if (!game.magic_wall_active)
+ element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
+ Store[newx][newy] = Store[x][y];
+ }
+ else if (element == EL_DC_MAGIC_WALL_EMPTYING)
+ {
+ Feld[x][y] = get_next_element(element);
+ if (!game.magic_wall_active)
+ Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
+ element = Feld[newx][newy] = Store[x][y];
+
+ InitField(newx, newy, FALSE);
}
else if (element == EL_AMOEBA_DROPPING)
{
MovDelay[newx][newy] = 0;
-#if 1
if (CAN_CHANGE_OR_HAS_ACTION(element))
-#else
- if (CAN_CHANGE(element))
-#endif
{
/* copy element change control values to new field */
ChangeDelay[newx][newy] = ChangeDelay[x][y];
ChangePage[newx][newy] = ChangePage[x][y];
ChangeCount[newx][newy] = ChangeCount[x][y];
ChangeEvent[newx][newy] = ChangeEvent[x][y];
-
-#if 0
-#if USE_NEW_CUSTOM_VALUE
- CustomValue[newx][newy] = CustomValue[x][y];
-#endif
-#endif
}
-#if 1
-#if USE_NEW_CUSTOM_VALUE
- CustomValue[newx][newy] = CustomValue[x][y];
-#endif
-#endif
+ CustomValue[newx][newy] = CustomValue[x][y];
ChangeDelay[x][y] = 0;
ChangePage[x][y] = -1;
ChangeCount[x][y] = 0;
ChangeEvent[x][y] = -1;
-#if USE_NEW_CUSTOM_VALUE
CustomValue[x][y] = 0;
-#endif
/* copy animation control values to new field */
GfxFrame[newx][newy] = GfxFrame[x][y];
Pushed[x][y] = Pushed[newx][newy] = FALSE;
/* some elements can leave other elements behind after moving */
-#if 1
if (ei->move_leave_element != EL_EMPTY &&
(ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
(!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
-#else
- if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
- (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
- (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
-#endif
{
int move_leave_element = ei->move_leave_element;
-#if 1
-#if 1
/* this makes it possible to leave the removed element again */
if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
-#else
- /* this makes it possible to leave the removed element again */
- if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
- move_leave_element = stored;
-#endif
-#else
- /* this makes it possible to leave the removed element again */
- if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
- ei->move_leave_element == EL_TRIGGER_ELEMENT)
- move_leave_element = stored;
-#endif
Feld[x][y] = move_leave_element;
InitField(x, y, FALSE);
if (GFX_CRUMBLED(Feld[x][y]))
- DrawLevelFieldCrumbledSandNeighbours(x, y);
+ TEST_DrawLevelFieldCrumbledNeighbours(x, y);
if (ELEM_IS_PLAYER(move_leave_element))
RelocatePlayer(x, y, move_leave_element);
element_info[element].move_pattern == MV_WHEN_DROPPED)))
GfxDir[x][y] = MovDir[newx][newy] = 0;
- DrawLevelField(x, y);
- DrawLevelField(newx, newy);
+ TEST_DrawLevelField(x, y);
+ TEST_DrawLevelField(newx, newy);
Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
int nextx = newx + dx, nexty = newy + dy;
boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
- WasJustMoving[newx][newy] = 3;
+ WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
if (CAN_FALL(element) && direction == MV_DOWN)
- WasJustFalling[newx][newy] = 3;
+ WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
- CheckCollision[newx][newy] = 2;
+ CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
+
+ if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
+ CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
}
if (DONT_TOUCH(element)) /* object may be nasty to player or others */
else if (element == EL_PENGUIN)
TestIfFriendTouchesBadThing(newx, newy);
+ if (DONT_GET_HIT_BY(element))
+ {
+ TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
+ }
+
/* give the player one last chance (one more frame) to move away */
if (CAN_FALL(element) && direction == MV_DOWN &&
(last_line || (!IS_FREE(x, newy + 1) &&
CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
TestIfElementTouchesCustomElement(x, y); /* empty or new element */
-
-#if 0
- if (ChangePage[newx][newy] != -1) /* delayed change */
- {
- int page = ChangePage[newx][newy];
- struct ElementChangeInfo *change = &ei->change_page[page];
-
- ChangePage[newx][newy] = -1;
-
- if (change->can_change)
- {
- if (ChangeElement(newx, newy, element, page))
- {
- if (change->post_change_function)
- change->post_change_function(newx, newy);
- }
- }
-
- if (change->has_action)
- ExecuteCustomElementAction(newx, newy, element, page);
- }
-#endif
-
TestIfElementHitsCustomElement(newx, newy, direction);
TestIfPlayerTouchesCustomElement(newx, newy);
TestIfElementTouchesCustomElement(newx, newy);
-#if 1
if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
- MV_DIR_OPPOSITE(direction));
-#endif
+ MV_DIR_OPPOSITE(direction));
}
int AmoebeNachbarNr(int ax, int ay)
AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
AmoebaCnt2[old_group_nr] = 0;
-#if 1
SCAN_PLAYFIELD(xx, yy)
-#else
- for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
-#endif
{
if (AmoebaNr[xx][yy] == old_group_nr)
AmoebaNr[xx][yy] = new_group_nr;
}
#endif
-#if 1
SCAN_PLAYFIELD(x, y)
-#else
- for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
-#endif
{
if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
{
}
#endif
-#if 1
SCAN_PLAYFIELD(x, y)
-#else
- for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
-#endif
{
if (AmoebaNr[x][y] == group_nr &&
(Feld[x][y] == EL_AMOEBA_DEAD ||
AmoebaNr[x][y] = 0;
Feld[x][y] = new_element;
InitField(x, y, FALSE);
- DrawLevelField(x, y);
+ TEST_DrawLevelField(x, y);
done = TRUE;
}
}
void AmoebeWaechst(int x, int y)
{
- static unsigned long sound_delay = 0;
- static unsigned long sound_delay_value = 0;
+ static unsigned int sound_delay = 0;
+ static unsigned int sound_delay_value = 0;
if (!MovDelay[x][y]) /* start new growing cycle */
{
{
Feld[x][y] = Store[x][y];
Store[x][y] = 0;
- DrawLevelField(x, y);
+ TEST_DrawLevelField(x, y);
}
}
}
void AmoebaDisappearing(int x, int y)
{
- static unsigned long sound_delay = 0;
- static unsigned long sound_delay_value = 0;
+ static unsigned int sound_delay = 0;
+ static unsigned int sound_delay_value = 0;
if (!MovDelay[x][y]) /* start new shrinking cycle */
{
if (!MovDelay[x][y])
{
Feld[x][y] = EL_EMPTY;
- DrawLevelField(x, y);
+ TEST_DrawLevelField(x, y);
/* don't let mole enter this field in this cycle;
(give priority to objects falling to this field from above) */
if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
{
Feld[ax][ay] = EL_AMOEBA_DEAD;
- DrawLevelField(ax, ay);
+ TEST_DrawLevelField(ax, ay);
return;
}
if (IS_FREE(x, y) ||
CAN_GROW_INTO(Feld[x][y]) ||
- Feld[x][y] == EL_QUICKSAND_EMPTY)
+ Feld[x][y] == EL_QUICKSAND_EMPTY ||
+ Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
{
newax = x;
neway = y;
if (IS_FREE(x, y) ||
CAN_GROW_INTO(Feld[x][y]) ||
- Feld[x][y] == EL_QUICKSAND_EMPTY)
+ Feld[x][y] == EL_QUICKSAND_EMPTY ||
+ Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
{
newax = x;
neway = y;
if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
{
Feld[ax][ay] = EL_AMOEBA_DEAD;
- DrawLevelField(ax, ay);
+ TEST_DrawLevelField(ax, ay);
AmoebaCnt[AmoebaNr[ax][ay]]--;
if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
return;
}
- DrawLevelField(newax, neway);
+ TEST_DrawLevelField(newax, neway);
}
void Life(int ax, int ay)
{
int x1, y1, x2, y2;
-#if 0
- static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
-#endif
int life_time = 40;
int element = Feld[ax][ay];
int graphic = el2img(element);
{
Feld[xx][yy] = EL_EMPTY;
if (!Stop[xx][yy])
- DrawLevelField(xx, yy);
+ TEST_DrawLevelField(xx, yy);
Stop[xx][yy] = TRUE;
changed = TRUE;
}
Feld[xx][yy] = element;
MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
if (!Stop[xx][yy])
- DrawLevelField(xx, yy);
+ TEST_DrawLevelField(xx, yy);
Stop[xx][yy] = TRUE;
changed = TRUE;
}
static void StopRobotWheel(int x, int y)
{
if (ZX == x && ZY == y)
+ {
ZX = ZY = -1;
+
+ game.robot_wheel_active = FALSE;
+ }
}
static void InitTimegateWheel(int x, int y)
static void RunTimegateWheel(int x, int y)
{
- PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
+ PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
}
static void InitMagicBallDelay(int x, int y)
{
-#if 1
ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
-#else
- ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
-#endif
}
static void ActivateMagicBall(int bx, int by)
game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
}
-static void InitDiagonalMovingElement(int x, int y)
-{
-#if 0
- MovDelay[x][y] = level.android_move_time;
-#endif
-}
-
void CheckExit(int x, int y)
{
if (local_player->gems_still_needed > 0 ||
PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
}
+void CheckExitEM(int x, int y)
+{
+ if (local_player->gems_still_needed > 0 ||
+ local_player->sokobanfields_still_needed > 0 ||
+ local_player->lights_still_needed > 0)
+ {
+ int element = Feld[x][y];
+ int graphic = el2img(element);
+
+ if (IS_ANIMATED(graphic))
+ DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
+
+ return;
+ }
+
+ if (AllPlayersGone) /* do not re-open exit door closed after last player */
+ return;
+
+ Feld[x][y] = EL_EM_EXIT_OPENING;
+
+ PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
+}
+
+void CheckExitSteel(int x, int y)
+{
+ if (local_player->gems_still_needed > 0 ||
+ local_player->sokobanfields_still_needed > 0 ||
+ local_player->lights_still_needed > 0)
+ {
+ int element = Feld[x][y];
+ int graphic = el2img(element);
+
+ if (IS_ANIMATED(graphic))
+ DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
+
+ return;
+ }
+
+ if (AllPlayersGone) /* do not re-open exit door closed after last player */
+ return;
+
+ Feld[x][y] = EL_STEEL_EXIT_OPENING;
+
+ PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
+}
+
+void CheckExitSteelEM(int x, int y)
+{
+ if (local_player->gems_still_needed > 0 ||
+ local_player->sokobanfields_still_needed > 0 ||
+ local_player->lights_still_needed > 0)
+ {
+ int element = Feld[x][y];
+ int graphic = el2img(element);
+
+ if (IS_ANIMATED(graphic))
+ DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
+
+ return;
+ }
+
+ if (AllPlayersGone) /* do not re-open exit door closed after last player */
+ return;
+
+ Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
+
+ PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
+}
+
void CheckExitSP(int x, int y)
{
if (local_player->gems_still_needed > 0)
{
int x, y;
-#if 1
SCAN_PLAYFIELD(x, y)
-#else
- for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
-#endif
{
int element = Feld[x][y];
}
}
-void EdelsteinFunkeln(int x, int y)
+void DrawTwinkleOnField(int x, int y)
{
if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
return;
return;
if (MovDelay[x][y] == 0) /* next animation frame */
- MovDelay[x][y] = 11 * !SimpleRND(500);
+ MovDelay[x][y] = 11 * !GetSimpleRandom(500);
if (MovDelay[x][y] != 0) /* wait some time before next frame */
{
MovDelay[x][y]--;
- if (setup.direct_draw && MovDelay[x][y])
- SetDrawtoField(DRAW_BUFFERED);
-
DrawLevelElementAnimation(x, y, Feld[x][y]);
if (MovDelay[x][y] != 0)
10 - MovDelay[x][y]);
DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
-
- if (setup.direct_draw)
- {
- int dest_x, dest_y;
-
- dest_x = FX + SCREENX(x) * TILEX;
- dest_y = FY + SCREENY(y) * TILEY;
-
- BlitBitmap(drawto_field, window,
- dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
- SetDrawtoField(DRAW_DIRECT);
- }
}
}
}
if (MovDir[x][y] == MV_LEFT)
{
if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
- DrawLevelField(x - 1, y);
+ TEST_DrawLevelField(x - 1, y);
}
else if (MovDir[x][y] == MV_RIGHT)
{
if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
- DrawLevelField(x + 1, y);
+ TEST_DrawLevelField(x + 1, y);
}
else if (MovDir[x][y] == MV_UP)
{
if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
- DrawLevelField(x, y - 1);
+ TEST_DrawLevelField(x, y - 1);
}
else
{
if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
- DrawLevelField(x, y + 1);
+ TEST_DrawLevelField(x, y + 1);
}
Feld[x][y] = Store[x][y];
Store[x][y] = 0;
GfxDir[x][y] = MovDir[x][y] = MV_NONE;
- DrawLevelField(x, y);
+ TEST_DrawLevelField(x, y);
}
}
}
if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
element == EL_EXPANDABLE_WALL_ANY ||
- element == EL_EXPANDABLE_WALL)
+ element == EL_EXPANDABLE_WALL ||
+ element == EL_BD_EXPANDABLE_WALL)
{
if (links_frei)
{
}
if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
- DrawLevelField(ax, ay);
+ TEST_DrawLevelField(ax, ay);
if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
oben_massiv = TRUE;
PlayLevelSoundAction(ax, ay, ACTION_GROWING);
}
-void CheckForDragon(int x, int y)
+void MauerAblegerStahl(int ax, int ay)
{
- int i, j;
- boolean dragon_found = FALSE;
- static int xy[4][2] =
- {
- { 0, -1 },
- { -1, 0 },
- { +1, 0 },
- { 0, +1 }
- };
+ int element = Feld[ax][ay];
+ int graphic = el2img(element);
+ boolean oben_frei = FALSE, unten_frei = FALSE;
+ boolean links_frei = FALSE, rechts_frei = FALSE;
+ boolean oben_massiv = FALSE, unten_massiv = FALSE;
+ boolean links_massiv = FALSE, rechts_massiv = FALSE;
+ boolean new_wall = FALSE;
- for (i = 0; i < NUM_DIRECTIONS; i++)
- {
- for (j = 0; j < 4; j++)
- {
- int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
+ if (IS_ANIMATED(graphic))
+ DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
- if (IN_LEV_FIELD(xx, yy) &&
- (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
- {
- if (Feld[xx][yy] == EL_DRAGON)
- dragon_found = TRUE;
- }
- else
- break;
- }
+ if (!MovDelay[ax][ay]) /* start building new wall */
+ MovDelay[ax][ay] = 6;
+
+ if (MovDelay[ax][ay]) /* wait some time before building new wall */
+ {
+ MovDelay[ax][ay]--;
+ if (MovDelay[ax][ay])
+ return;
}
- if (!dragon_found)
+ if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
+ oben_frei = TRUE;
+ if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
+ unten_frei = TRUE;
+ if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
+ links_frei = TRUE;
+ if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
+ rechts_frei = TRUE;
+
+ if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
+ element == EL_EXPANDABLE_STEELWALL_ANY)
{
- for (i = 0; i < NUM_DIRECTIONS; i++)
+ if (oben_frei)
{
- for (j = 0; j < 3; j++)
- {
- int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
-
- if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
- {
- Feld[xx][yy] = EL_EMPTY;
- DrawLevelField(xx, yy);
- }
- else
- break;
- }
+ Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
+ Store[ax][ay-1] = element;
+ GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
+ if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
+ DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
+ IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
+ new_wall = TRUE;
+ }
+ if (unten_frei)
+ {
+ Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
+ Store[ax][ay+1] = element;
+ GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
+ if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
+ DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
+ IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
+ new_wall = TRUE;
}
}
-}
-static void InitBuggyBase(int x, int y)
-{
+ if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
+ element == EL_EXPANDABLE_STEELWALL_ANY)
+ {
+ if (links_frei)
+ {
+ Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
+ Store[ax-1][ay] = element;
+ GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
+ if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
+ DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
+ IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
+ new_wall = TRUE;
+ }
+
+ if (rechts_frei)
+ {
+ Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
+ Store[ax+1][ay] = element;
+ GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
+ if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
+ DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
+ IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
+ new_wall = TRUE;
+ }
+ }
+
+ if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
+ oben_massiv = TRUE;
+ if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
+ unten_massiv = TRUE;
+ if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
+ links_massiv = TRUE;
+ if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
+ rechts_massiv = TRUE;
+
+ if (((oben_massiv && unten_massiv) ||
+ element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
+ ((links_massiv && rechts_massiv) ||
+ element == EL_EXPANDABLE_STEELWALL_VERTICAL))
+ Feld[ax][ay] = EL_STEELWALL;
+
+ if (new_wall)
+ PlayLevelSoundAction(ax, ay, ACTION_GROWING);
+}
+
+void CheckForDragon(int x, int y)
+{
+ int i, j;
+ boolean dragon_found = FALSE;
+ static int xy[4][2] =
+ {
+ { 0, -1 },
+ { -1, 0 },
+ { +1, 0 },
+ { 0, +1 }
+ };
+
+ for (i = 0; i < NUM_DIRECTIONS; i++)
+ {
+ for (j = 0; j < 4; j++)
+ {
+ int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
+
+ if (IN_LEV_FIELD(xx, yy) &&
+ (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
+ {
+ if (Feld[xx][yy] == EL_DRAGON)
+ dragon_found = TRUE;
+ }
+ else
+ break;
+ }
+ }
+
+ if (!dragon_found)
+ {
+ for (i = 0; i < NUM_DIRECTIONS; i++)
+ {
+ for (j = 0; j < 3; j++)
+ {
+ int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
+
+ if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
+ {
+ Feld[xx][yy] = EL_EMPTY;
+ TEST_DrawLevelField(xx, yy);
+ }
+ else
+ break;
+ }
+ }
+ }
+}
+
+static void InitBuggyBase(int x, int y)
+{
int element = Feld[x][y];
int activating_delay = FRAMES_PER_SECOND / 4;
for (i = 0; i < NUM_DIRECTIONS; i++)
{
- int xx = x + xy[i][0], yy = y + xy[i][1];
+ int xx = x + xy[i][0];
+ int yy = y + xy[i][1];
- if (IS_PLAYER(xx, yy))
+ if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
{
PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
/* if new animation frame was drawn, correct crumbled sand border */
if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
- DrawLevelFieldCrumbledSand(x, y);
+ TEST_DrawLevelFieldCrumbled(x, y);
}
static int getSpecialActionElement(int element, int number, int base_element)
{
struct ElementInfo *ei = &element_info[element];
struct ElementChangeInfo *change = &ei->change_page[page];
+ int target_element = change->target_element;
int action_type = change->action_type;
int action_mode = change->action_mode;
int action_arg = change->action_arg;
+ int action_element = change->action_element;
int i;
if (!change->has_action)
(level.time > 0 ? TimeLeft :
TimePlayed);
- int action_arg_element =
+ int action_arg_element_raw =
(action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
+ action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
+ action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
+ action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
+ action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
EL_EMPTY);
+ int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
int action_arg_direction =
(action_arg >= CA_ARG_DIRECTION_LEFT &&
action_type == CA_SET_LEVEL_GEMS ? 999 :
action_type == CA_SET_LEVEL_TIME ? 9999 :
action_type == CA_SET_LEVEL_SCORE ? 99999 :
- action_type == CA_SET_CE_SCORE ? 9999 :
action_type == CA_SET_CE_VALUE ? 9999 :
+ action_type == CA_SET_CE_SCORE ? 9999 :
CA_ARG_MAX);
int action_arg_number_reset =
- (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize :
+ (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
action_type == CA_SET_LEVEL_TIME ? level.time :
action_type == CA_SET_LEVEL_SCORE ? 0 :
+ action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
action_type == CA_SET_CE_SCORE ? 0 :
-#if 1
- action_type == CA_SET_CE_VALUE ? GET_NEW_CUSTOM_VALUE(element) :
-#else
- action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
-#endif
0);
int action_arg_number =
action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
- action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
-#if USE_NEW_CUSTOM_VALUE
action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
-#else
- action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
-#endif
+ action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
- action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CUSTOM_VALUE(change->target_element) :
+ action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
+ action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
+ action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
+ action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
+ action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
+ action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
-1);
int action_arg_number_old =
(action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
action_type == CA_SET_LEVEL_TIME ? TimeLeft :
action_type == CA_SET_LEVEL_SCORE ? local_player->score :
- action_type == CA_SET_CE_SCORE ? ei->collect_score :
action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
+ action_type == CA_SET_CE_SCORE ? ei->collect_score :
0);
int action_arg_number_new =
action_arg_number_min, action_arg_number_max);
int trigger_player_bits =
- (change->actual_trigger_player >= EL_PLAYER_1 &&
- change->actual_trigger_player <= EL_PLAYER_4 ?
- (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
- PLAYER_BITS_ANY);
+ (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
+ change->actual_trigger_player_bits : change->trigger_player);
int action_arg_player_bits =
(action_arg >= CA_ARG_PLAYER_1 &&
action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
+ action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
PLAYER_BITS_ANY);
/* ---------- execute action -------------------------------------------- */
- switch(action_type)
+ switch (action_type)
{
case CA_NO_ACTION:
{
{
TimeLeft = action_arg_number_new;
- DrawGameValue_Time(TimeLeft);
+ game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
+
+ DisplayGameControlValues();
if (!TimeLeft && setup.time_limit)
for (i = 0; i < MAX_PLAYERS; i++)
{
local_player->score = action_arg_number_new;
- DrawGameValue_Score(local_player->score);
+ game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
+
+ DisplayGameControlValues();
break;
}
{
local_player->gems_still_needed = action_arg_number_new;
- DrawGameValue_Emeralds(local_player->gems_still_needed);
+ game.snapshot.collected_item = TRUE;
+
+ game_panel_controls[GAME_PANEL_GEMS].value =
+ local_player->gems_still_needed;
+
+ DisplayGameControlValues();
break;
}
- case CA_SET_LEVEL_GRAVITY:
+ case CA_SET_LEVEL_WIND:
{
- game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
- action_arg == CA_ARG_GRAVITY_ON ? TRUE :
- action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
- game.gravity);
+ game.wind_direction = action_arg_direction;
+
break;
}
- case CA_SET_LEVEL_WIND:
+ case CA_SET_LEVEL_RANDOM_SEED:
{
- game.wind_direction = action_arg_direction;
+ /* ensure that setting a new random seed while playing is predictable */
+ InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
break;
}
{
for (i = 0; i < MAX_PLAYERS; i++)
if (action_arg_player_bits & (1 << i))
- stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
+ PlayerWins(&stored_player[i]);
break;
}
{
stored_player[i].key[KEY_NR(element)] = key_state;
- DrawGameValue_Keys(stored_player[i].key);
-
- redraw_mask |= REDRAW_DOOR_1;
+ DrawGameDoorValues();
}
}
}
action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
CA_MODE_MULTIPLY);
}
+ else if (action_arg == CA_ARG_NUMBER_RESET)
+ {
+ action_arg_number = level.initial_player_stepsize[i];
+ }
move_stepsize =
getModifiedActionNumber(move_stepsize,
action_arg_number_min,
action_arg_number_max);
-#if 1
SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
-#else
- /* make sure that value is power of 2 */
- move_stepsize = (1 << log_2(move_stepsize));
-
- /* do no immediately change -- the player might just be moving */
- stored_player[i].move_delay_value_next = TILEX / move_stepsize;
-
- stored_player[i].cannot_move =
- (action_arg == CA_ARG_SPEED_NOT_MOVING ? TRUE : FALSE);
-#endif
}
}
break;
}
+ case CA_SET_PLAYER_GRAVITY:
+ {
+ for (i = 0; i < MAX_PLAYERS; i++)
+ {
+ if (trigger_player_bits & (1 << i))
+ {
+ stored_player[i].gravity =
+ (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
+ action_arg == CA_ARG_GRAVITY_ON ? TRUE :
+ action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
+ stored_player[i].gravity);
+ }
+ }
+
+ break;
+ }
+
case CA_SET_PLAYER_ARTWORK:
{
for (i = 0; i < MAX_PLAYERS; i++)
(level.use_artwork_element[i] ? level.artwork_element[i] :
stored_player[i].element_nr);
+ if (stored_player[i].artwork_element != artwork_element)
+ stored_player[i].Frame = 0;
+
stored_player[i].artwork_element = artwork_element;
SetPlayerWaiting(&stored_player[i], FALSE);
break;
}
+ case CA_SET_PLAYER_INVENTORY:
+ {
+ for (i = 0; i < MAX_PLAYERS; i++)
+ {
+ struct PlayerInfo *player = &stored_player[i];
+ int j, k;
+
+ if (trigger_player_bits & (1 << i))
+ {
+ int inventory_element = action_arg_element;
+
+ if (action_arg == CA_ARG_ELEMENT_TARGET ||
+ action_arg == CA_ARG_ELEMENT_TRIGGER ||
+ action_arg == CA_ARG_ELEMENT_ACTION)
+ {
+ int element = inventory_element;
+ int collect_count = element_info[element].collect_count_initial;
+
+ if (!IS_CUSTOM_ELEMENT(element))
+ collect_count = 1;
+
+ if (collect_count == 0)
+ player->inventory_infinite_element = element;
+ else
+ for (k = 0; k < collect_count; k++)
+ if (player->inventory_size < MAX_INVENTORY_SIZE)
+ player->inventory_element[player->inventory_size++] =
+ element;
+ }
+ else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
+ action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
+ action_arg == CA_ARG_INVENTORY_RM_ACTION)
+ {
+ if (player->inventory_infinite_element != EL_UNDEFINED &&
+ IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
+ action_arg_element_raw))
+ player->inventory_infinite_element = EL_UNDEFINED;
+
+ for (k = 0, j = 0; j < player->inventory_size; j++)
+ {
+ if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
+ action_arg_element_raw))
+ player->inventory_element[k++] = player->inventory_element[j];
+ }
+
+ player->inventory_size = k;
+ }
+ else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
+ {
+ if (player->inventory_size > 0)
+ {
+ for (j = 0; j < player->inventory_size - 1; j++)
+ player->inventory_element[j] = player->inventory_element[j + 1];
+
+ player->inventory_size--;
+ }
+ }
+ else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
+ {
+ if (player->inventory_size > 0)
+ player->inventory_size--;
+ }
+ else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
+ {
+ player->inventory_infinite_element = EL_UNDEFINED;
+ player->inventory_size = 0;
+ }
+ else if (action_arg == CA_ARG_INVENTORY_RESET)
+ {
+ player->inventory_infinite_element = EL_UNDEFINED;
+ player->inventory_size = 0;
+
+ if (level.use_initial_inventory[i])
+ {
+ for (j = 0; j < level.initial_inventory_size[i]; j++)
+ {
+ int element = level.initial_inventory_content[i][j];
+ int collect_count = element_info[element].collect_count_initial;
+
+ if (!IS_CUSTOM_ELEMENT(element))
+ collect_count = 1;
+
+ if (collect_count == 0)
+ player->inventory_infinite_element = element;
+ else
+ for (k = 0; k < collect_count; k++)
+ if (player->inventory_size < MAX_INVENTORY_SIZE)
+ player->inventory_element[player->inventory_size++] =
+ element;
+ }
+ }
+ }
+ }
+ }
+
+ break;
+ }
+
/* ---------- CE actions ---------------------------------------------- */
+ case CA_SET_CE_VALUE:
+ {
+ int last_ce_value = CustomValue[x][y];
+
+ CustomValue[x][y] = action_arg_number_new;
+
+ if (CustomValue[x][y] != last_ce_value)
+ {
+ CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
+ CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
+
+ if (CustomValue[x][y] == 0)
+ {
+ CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
+ CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
+ }
+ }
+
+ break;
+ }
+
case CA_SET_CE_SCORE:
{
+ int last_ce_score = ei->collect_score;
+
ei->collect_score = action_arg_number_new;
+ if (ei->collect_score != last_ce_score)
+ {
+ CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
+ CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
+
+ if (ei->collect_score == 0)
+ {
+ int xx, yy;
+
+ CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
+ CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
+
+ /*
+ This is a very special case that seems to be a mixture between
+ CheckElementChange() and CheckTriggeredElementChange(): while
+ the first one only affects single elements that are triggered
+ directly, the second one affects multiple elements in the playfield
+ that are triggered indirectly by another element. This is a third
+ case: Changing the CE score always affects multiple identical CEs,
+ so every affected CE must be checked, not only the single CE for
+ which the CE score was changed in the first place (as every instance
+ of that CE shares the same CE score, and therefore also can change)!
+ */
+ SCAN_PLAYFIELD(xx, yy)
+ {
+ if (Feld[xx][yy] == element)
+ CheckElementChange(xx, yy, element, EL_UNDEFINED,
+ CE_SCORE_GETS_ZERO);
+ }
+ }
+ }
+
break;
}
- case CA_SET_CE_VALUE:
+ case CA_SET_CE_ARTWORK:
{
-#if USE_NEW_CUSTOM_VALUE
- int last_custom_value = CustomValue[x][y];
+ int artwork_element = action_arg_element;
+ boolean reset_frame = FALSE;
+ int xx, yy;
- CustomValue[x][y] = action_arg_number_new;
+ if (action_arg == CA_ARG_ELEMENT_RESET)
+ artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
+ element);
-#if 0
- printf("::: Count == %d\n", CustomValue[x][y]);
-#endif
+ if (ei->gfx_element != artwork_element)
+ reset_frame = TRUE;
- if (CustomValue[x][y] == 0 && last_custom_value > 0)
- {
-#if 0
- printf("::: CE_VALUE_GETS_ZERO\n");
-#endif
+ ei->gfx_element = artwork_element;
- CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
- CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
+ SCAN_PLAYFIELD(xx, yy)
+ {
+ if (Feld[xx][yy] == element)
+ {
+ if (reset_frame)
+ {
+ ResetGfxAnimation(xx, yy);
+ ResetRandomAnimationValue(xx, yy);
+ }
-#if 0
- printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
-#endif
+ TEST_DrawLevelField(xx, yy);
+ }
}
-#endif
break;
}
static void CreateFieldExt(int x, int y, int element, boolean is_change)
{
+ int old_element = Feld[x][y];
+ int new_element = GetElementFromGroupElement(element);
int previous_move_direction = MovDir[x][y];
-#if USE_NEW_CUSTOM_VALUE
int last_ce_value = CustomValue[x][y];
-#endif
- boolean add_player = (ELEM_IS_PLAYER(element) &&
- IS_WALKABLE(Feld[x][y]));
-
- /* check if element under player changes from accessible to unaccessible
- (needed for special case of dropping element which then changes) */
- if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
- IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(element))
- {
- Bang(x, y);
-
- return;
- }
+ boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
+ boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
+ boolean add_player_onto_element = (new_element_is_player &&
+ new_element != EL_SOKOBAN_FIELD_PLAYER &&
+ IS_WALKABLE(old_element));
- if (!add_player)
+ if (!add_player_onto_element)
{
if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
RemoveMovingField(x, y);
else
RemoveField(x, y);
- Feld[x][y] = element;
+ Feld[x][y] = new_element;
- ResetGfxAnimation(x, y);
- ResetRandomAnimationValue(x, y);
-
- if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
+ if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
MovDir[x][y] = previous_move_direction;
-#if USE_NEW_CUSTOM_VALUE
- if (element_info[Feld[x][y]].use_last_ce_value)
+ if (element_info[new_element].use_last_ce_value)
CustomValue[x][y] = last_ce_value;
-#endif
InitField_WithBug1(x, y, FALSE);
- DrawLevelField(x, y);
+ new_element = Feld[x][y]; /* element may have changed */
- if (GFX_CRUMBLED(Feld[x][y]))
- DrawLevelFieldCrumbledSandNeighbours(x, y);
+ ResetGfxAnimation(x, y);
+ ResetRandomAnimationValue(x, y);
+
+ TEST_DrawLevelField(x, y);
+
+ if (GFX_CRUMBLED(new_element))
+ TEST_DrawLevelFieldCrumbledNeighbours(x, y);
+ }
+
+ /* check if element under the player changes from accessible to unaccessible
+ (needed for special case of dropping element which then changes) */
+ /* (must be checked after creating new element for walkable group elements) */
+ if (IS_PLAYER(x, y) && !player_explosion_protected &&
+ IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
+ {
+ Bang(x, y);
+
+ return;
}
/* "ChangeCount" not set yet to allow "entered by player" change one time */
- if (ELEM_IS_PLAYER(element))
- RelocatePlayer(x, y, element);
+ if (new_element_is_player)
+ RelocatePlayer(x, y, new_element);
if (is_change)
ChangeCount[x][y]++; /* count number of changes in the same frame */
{
element = GET_VALID_RUNTIME_ELEMENT(element);
-#if USE_STOP_CHANGED_ELEMENTS
if (game.engine_version >= VERSION_IDENT(3,2,0,7))
{
int old_element = Feld[x][y];
(!CAN_MOVE(old_element) || !CAN_MOVE(element)))
Stop[x][y] = TRUE;
}
-#endif
CreateFieldExt(x, y, element, TRUE);
}
static boolean ChangeElement(int x, int y, int element, int page)
{
- struct ElementChangeInfo *change = &element_info[element].change_page[page];
+ struct ElementInfo *ei = &element_info[element];
+ struct ElementChangeInfo *change = &ei->change_page[page];
+ int ce_value = CustomValue[x][y];
+ int ce_score = ei->collect_score;
int target_element;
int old_element = Feld[x][y];
{
/* reset actual trigger element, trigger player and action element */
change->actual_trigger_element = EL_EMPTY;
- change->actual_trigger_player = EL_PLAYER_1;
+ change->actual_trigger_player = EL_EMPTY;
+ change->actual_trigger_player_bits = CH_PLAYER_NONE;
change->actual_trigger_side = CH_SIDE_NONE;
change->actual_trigger_ce_value = 0;
+ change->actual_trigger_ce_score = 0;
}
/* do not change elements more than a specified maximum number of changes */
ChangeEvent[ex][ey] = ChangeEvent[x][y];
content_element = change->target_content.e[xx][yy];
- target_element = GET_TARGET_ELEMENT(content_element, change);
+ target_element = GET_TARGET_ELEMENT(element, content_element, change,
+ ce_value, ce_score);
CreateElementFromChange(ex, ey, target_element);
}
else
{
- target_element = GET_TARGET_ELEMENT(change->target_element, change);
+ target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
+ ce_value, ce_score);
if (element == EL_DIAGONAL_GROWING ||
element == EL_DIAGONAL_SHRINKING)
return TRUE;
}
-#if USE_NEW_DELAYED_ACTION
-
static void HandleElementChange(int x, int y, int page)
{
int element = MovingOrBlocked2Element(x, y);
struct ElementInfo *ei = &element_info[element];
struct ElementChangeInfo *change = &ei->change_page[page];
+ boolean handle_action_before_change = FALSE;
#ifdef DEBUG
if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
/* this can happen with classic bombs on walkable, changing elements */
if (!CAN_CHANGE_OR_HAS_ACTION(element))
{
-#if 0
- if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
- ChangeDelay[x][y] = 0;
-#endif
-
return;
}
if (change->can_change)
{
- ResetGfxAnimation(x, y);
- ResetRandomAnimationValue(x, y);
+ /* !!! not clear why graphic animation should be reset at all here !!! */
+ /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
+ /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
+
+ /*
+ GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
+
+ When using an animation frame delay of 1 (this only happens with
+ "sp_zonk.moving.left/right" in the classic graphics), the default
+ (non-moving) animation shows wrong animation frames (while the
+ moving animation, like "sp_zonk.moving.left/right", is correct,
+ so this graphical bug never shows up with the classic graphics).
+ For an animation with 4 frames, this causes wrong frames 0,0,1,2
+ be drawn instead of the correct frames 0,1,2,3. This is caused by
+ "GfxFrame[][]" being reset *twice* (in two successive frames) after
+ an element change: First when the change delay ("ChangeDelay[][]")
+ counter has reached zero after decrementing, then a second time in
+ the next frame (after "GfxFrame[][]" was already incremented) when
+ "ChangeDelay[][]" is reset to the initial delay value again.
+
+ This causes frame 0 to be drawn twice, while the last frame won't
+ be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
+
+ As some animations may already be cleverly designed around this bug
+ (at least the "Snake Bite" snake tail animation does this), it cannot
+ simply be fixed here without breaking such existing animations.
+ Unfortunately, it cannot easily be detected if a graphics set was
+ designed "before" or "after" the bug was fixed. As a workaround,
+ a new graphics set option "game.graphics_engine_version" was added
+ to be able to specify the game's major release version for which the
+ graphics set was designed, which can then be used to decide if the
+ bugfix should be used (version 4 and above) or not (version 3 or
+ below, or if no version was specified at all, as with old sets).
+
+ (The wrong/fixed animation frames can be tested with the test level set
+ "test_gfxframe" and level "000", which contains a specially prepared
+ custom element at level position (x/y) == (11/9) which uses the zonk
+ animation mentioned above. Using "game.graphics_engine_version: 4"
+ fixes the wrong animation frames, showing the correct frames 0,1,2,3.
+ This can also be seen from the debug output for this test element.)
+ */
+
+ /* when a custom element is about to change (for example by change delay),
+ do not reset graphic animation when the custom element is moving */
+ if (game.graphics_engine_version < 4 &&
+ !IS_MOVING(x, y))
+ {
+ ResetGfxAnimation(x, y);
+ ResetRandomAnimationValue(x, y);
+ }
if (change->pre_change_function)
change->pre_change_function(x, y);
return;
}
+ /* special case: set new level random seed before changing element */
+ if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
+ handle_action_before_change = TRUE;
+
+ if (change->has_action && handle_action_before_change)
+ ExecuteCustomElementAction(x, y, element, page);
+
if (change->can_change)
{
if (ChangeElement(x, y, element, page))
}
}
- if (change->has_action)
+ if (change->has_action && !handle_action_before_change)
ExecuteCustomElementAction(x, y, element, page);
}
}
-#else
-
-static void HandleElementChange(int x, int y, int page)
-{
- int element = MovingOrBlocked2Element(x, y);
- struct ElementInfo *ei = &element_info[element];
- struct ElementChangeInfo *change = &ei->change_page[page];
-
-#ifdef DEBUG
- if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
- {
- printf("\n\n");
- printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
- x, y, element, element_info[element].token_name);
- printf("HandleElementChange(): This should never happen!\n");
- printf("\n\n");
- }
-#endif
-
- /* this can happen with classic bombs on walkable, changing elements */
- if (!CAN_CHANGE(element))
- {
-#if 0
- if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
- ChangeDelay[x][y] = 0;
-#endif
-
- return;
- }
-
- if (ChangeDelay[x][y] == 0) /* initialize element change */
- {
- ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
-
- ResetGfxAnimation(x, y);
- ResetRandomAnimationValue(x, y);
-
- if (change->pre_change_function)
- change->pre_change_function(x, y);
- }
-
- ChangeDelay[x][y]--;
-
- if (ChangeDelay[x][y] != 0) /* continue element change */
- {
- int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
-
- if (IS_ANIMATED(graphic))
- DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
-
- if (change->change_function)
- change->change_function(x, y);
- }
- else /* finish element change */
- {
- if (ChangePage[x][y] != -1) /* remember page from delayed change */
- {
- page = ChangePage[x][y];
- ChangePage[x][y] = -1;
-
- change = &ei->change_page[page];
- }
-
- if (IS_MOVING(x, y)) /* never change a running system ;-) */
- {
- ChangeDelay[x][y] = 1; /* try change after next move step */
- ChangePage[x][y] = page; /* remember page to use for change */
-
- return;
- }
-
- if (ChangeElement(x, y, element, page))
- {
- if (change->post_change_function)
- change->post_change_function(x, y);
- }
- }
-}
-
-#endif
-
static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
int trigger_element,
int trigger_event,
if (!(trigger_events[trigger_element][trigger_event]))
return FALSE;
+ RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
+
for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
{
int element = EL_CUSTOM_START + i;
IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
{
change->actual_trigger_element = trigger_element;
- change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
+ change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
+ change->actual_trigger_player_bits = trigger_player;
change->actual_trigger_side = trigger_side;
change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
+ change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
if ((change->can_change && !change_done) || change->has_action)
{
int x, y;
-#if 1
SCAN_PLAYFIELD(x, y)
-#else
- for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
-#endif
{
if (Feld[x][y] == element)
{
if (change->can_change && !change_done)
{
+ /* if element already changed in this frame, not only prevent
+ another element change (checked in ChangeElement()), but
+ also prevent additional element actions for this element */
+
+ if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
+ !level.use_action_after_change_bug)
+ continue;
+
ChangeDelay[x][y] = 1;
ChangeEvent[x][y] = trigger_event;
HandleElementChange(x, y, p);
}
-#if USE_NEW_DELAYED_ACTION
else if (change->has_action)
{
+ /* if element already changed in this frame, not only prevent
+ another element change (checked in ChangeElement()), but
+ also prevent additional element actions for this element */
+
+ if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
+ !level.use_action_after_change_bug)
+ continue;
+
ExecuteCustomElementAction(x, y, element, p);
PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
}
-#else
- if (change->has_action)
- {
- ExecuteCustomElementAction(x, y, element, p);
- PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
- }
-#endif
}
}
}
}
+ RECURSION_LOOP_DETECTION_END();
+
return change_done_any;
}
element = Feld[x][y];
}
-#if 0
- /* check if element has already changed */
- if (Feld[x][y] != element)
- return FALSE;
-#else
/* check if element has already changed or is about to change after moving */
if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
Feld[x][y] != element) ||
(ChangeCount[x][y] >= game.max_num_changes_per_frame ||
ChangePage[x][y] != -1)))
return FALSE;
-#endif
+
+ RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
for (p = 0; p < element_info[element].num_change_pages; p++)
{
struct ElementChangeInfo *change = &element_info[element].change_page[p];
+ /* check trigger element for all events where the element that is checked
+ for changing interacts with a directly adjacent element -- this is
+ different to element changes that affect other elements to change on the
+ whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
boolean check_trigger_element =
(trigger_event == CE_TOUCHING_X ||
trigger_event == CE_HITTING_X ||
- trigger_event == CE_HIT_BY_X);
+ trigger_event == CE_HIT_BY_X ||
+ trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
if (change->can_change_or_has_action &&
change->has_event[trigger_event] &&
IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
{
change->actual_trigger_element = trigger_element;
- change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
+ change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
+ change->actual_trigger_player_bits = trigger_player;
change->actual_trigger_side = trigger_side;
change->actual_trigger_ce_value = CustomValue[x][y];
+ change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
/* special case: trigger element not at (x,y) position for some events */
if (check_trigger_element)
int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
change->actual_trigger_ce_value = CustomValue[xx][yy];
+ change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
}
if (change->can_change && !change_done)
change_done = TRUE;
}
-#if USE_NEW_DELAYED_ACTION
else if (change->has_action)
{
ExecuteCustomElementAction(x, y, element, p);
PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
}
-#else
- if (change->has_action)
- {
- ExecuteCustomElementAction(x, y, element, p);
- PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
- }
-#endif
}
}
+ RECURSION_LOOP_DETECTION_END();
+
return change_done;
}
boolean last_waiting = player->is_waiting;
int move_dir = player->MovDir;
+ player->dir_waiting = move_dir;
player->last_action_waiting = player->action_waiting;
if (is_waiting)
player->frame_counter_bored =
FrameCounter +
game.player_boring_delay_fixed +
- SimpleRND(game.player_boring_delay_random);
+ GetSimpleRandom(game.player_boring_delay_random);
player->frame_counter_sleeping =
FrameCounter +
game.player_sleeping_delay_fixed +
- SimpleRND(game.player_sleeping_delay_random);
+ GetSimpleRandom(game.player_sleeping_delay_random);
- InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
+ InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
}
if (game.player_sleeping_delay_fixed +
player->is_bored ? ACTION_BORING :
ACTION_WAITING);
+ if (player->is_sleeping && player->use_murphy)
+ {
+ /* special case for sleeping Murphy when leaning against non-free tile */
+
+ if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
+ (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
+ !IS_MOVING(player->jx - 1, player->jy)))
+ move_dir = MV_LEFT;
+ else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
+ (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
+ !IS_MOVING(player->jx + 1, player->jy)))
+ move_dir = MV_RIGHT;
+ else
+ player->is_sleeping = FALSE;
+
+ player->dir_waiting = move_dir;
+ }
+
if (player->is_sleeping)
{
if (player->num_special_action_sleeping > 0)
player->anim_delay_counter =
graphic_info[special_graphic].anim_delay_fixed +
- SimpleRND(graphic_info[special_graphic].anim_delay_random);
+ GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
player->post_delay_counter =
graphic_info[special_graphic].post_delay_fixed +
- SimpleRND(graphic_info[special_graphic].post_delay_random);
+ GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
player->special_action_sleeping = special_action;
}
if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
{
int special_action =
- ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
+ ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
int special_graphic =
el_act_dir2img(player->artwork_element, special_action, move_dir);
player->anim_delay_counter =
graphic_info[special_graphic].anim_delay_fixed +
- SimpleRND(graphic_info[special_graphic].anim_delay_random);
+ GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
player->post_delay_counter =
graphic_info[special_graphic].post_delay_fixed +
- SimpleRND(graphic_info[special_graphic].post_delay_random);
+ GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
player->special_action_bored = special_action;
}
player->anim_delay_counter = 0;
player->post_delay_counter = 0;
+ player->dir_waiting = player->MovDir;
player->action_waiting = ACTION_DEFAULT;
player->special_action_bored = ACTION_DEFAULT;
}
}
+static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
+{
+ static boolean player_was_moving = FALSE;
+ static boolean player_was_snapping = FALSE;
+ static boolean player_was_dropping = FALSE;
+
+ if ((!player->is_moving && player_was_moving) ||
+ (player->MovPos == 0 && player_was_moving) ||
+ (player->is_snapping && !player_was_snapping) ||
+ (player->is_dropping && !player_was_dropping))
+ {
+ if (!SaveEngineSnapshotToList())
+ return;
+
+ player_was_moving = FALSE;
+ player_was_snapping = TRUE;
+ player_was_dropping = TRUE;
+ }
+ else
+ {
+ if (player->is_moving)
+ player_was_moving = TRUE;
+
+ if (!player->is_snapping)
+ player_was_snapping = FALSE;
+
+ if (!player->is_dropping)
+ player_was_dropping = FALSE;
+ }
+}
+
+static void CheckSingleStepMode(struct PlayerInfo *player)
+{
+ if (tape.single_step && tape.recording && !tape.pausing)
+ {
+ /* as it is called "single step mode", just return to pause mode when the
+ player stopped moving after one tile (or never starts moving at all) */
+ if (!player->is_moving && !player->is_pushing)
+ {
+ TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
+ SnapField(player, 0, 0); /* stop snapping */
+ }
+ }
+
+ CheckSaveEngineSnapshot(player);
+}
+
static byte PlayerActions(struct PlayerInfo *player, byte player_action)
{
- boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
int left = player_action & JOY_LEFT;
int right = player_action & JOY_RIGHT;
int up = player_action & JOY_UP;
int down = player_action & JOY_DOWN;
int button1 = player_action & JOY_BUTTON_1;
int button2 = player_action & JOY_BUTTON_2;
- int dx = (left ? -1 : right ? 1 : 0);
- int dy = (up ? -1 : down ? 1 : 0);
+ int dx = (left ? -1 : right ? 1 : 0);
+ int dy = (up ? -1 : down ? 1 : 0);
if (!player->active || tape.pausing)
return 0;
if (player_action)
{
if (button1)
- snapped = SnapField(player, dx, dy);
+ SnapField(player, dx, dy);
else
{
if (button2)
- dropped = DropElement(player);
+ DropElement(player);
- moved = MovePlayer(player, dx, dy);
+ MovePlayer(player, dx, dy);
}
- if (tape.single_step && tape.recording && !tape.pausing)
- {
- if (button1 || (dropped && !moved))
- {
- TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
- SnapField(player, 0, 0); /* stop snapping */
- }
- }
+ CheckSingleStepMode(player);
SetPlayerWaiting(player, FALSE);
player->is_moving = FALSE;
player->is_dropping = FALSE;
+ player->is_dropping_pressed = FALSE;
+ player->drop_pressed_delay = 0;
+
+ CheckSingleStepMode(player);
return 0;
}
}
+static void CheckLevelTime()
+{
+ int i;
+
+ /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
+ if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+ {
+ if (level.native_em_level->lev->home == 0) /* all players at home */
+ {
+ PlayerWins(local_player);
+
+ AllPlayersGone = TRUE;
+
+ level.native_em_level->lev->home = -1;
+ }
+
+ if (level.native_em_level->ply[0]->alive == 0 &&
+ level.native_em_level->ply[1]->alive == 0 &&
+ level.native_em_level->ply[2]->alive == 0 &&
+ level.native_em_level->ply[3]->alive == 0) /* all dead */
+ AllPlayersGone = TRUE;
+ }
+ else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+ {
+ if (game_sp.LevelSolved &&
+ !game_sp.GameOver) /* game won */
+ {
+ PlayerWins(local_player);
+
+ game_sp.GameOver = TRUE;
+
+ AllPlayersGone = TRUE;
+ }
+
+ if (game_sp.GameOver) /* game lost */
+ AllPlayersGone = TRUE;
+ }
+
+ if (TimeFrames >= FRAMES_PER_SECOND)
+ {
+ TimeFrames = 0;
+ TapeTime++;
+
+ for (i = 0; i < MAX_PLAYERS; i++)
+ {
+ struct PlayerInfo *player = &stored_player[i];
+
+ if (SHIELD_ON(player))
+ {
+ player->shield_normal_time_left--;
+
+ if (player->shield_deadly_time_left > 0)
+ player->shield_deadly_time_left--;
+ }
+ }
+
+ if (!local_player->LevelSolved && !level.use_step_counter)
+ {
+ TimePlayed++;
+
+ if (TimeLeft > 0)
+ {
+ TimeLeft--;
+
+ if (TimeLeft <= 10 && setup.time_limit)
+ PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
+
+ /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
+ is reset from other values in UpdateGameDoorValues() -- FIX THIS */
+
+ game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
+
+ if (!TimeLeft && setup.time_limit)
+ {
+ if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+ level.native_em_level->lev->killed_out_of_time = TRUE;
+ else
+ for (i = 0; i < MAX_PLAYERS; i++)
+ KillPlayer(&stored_player[i]);
+ }
+ }
+ else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
+ {
+ game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
+ }
+
+ level.native_em_level->lev->time =
+ (game.no_time_limit ? TimePlayed : TimeLeft);
+ }
+
+ if (tape.recording || tape.playing)
+ DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
+ }
+
+ if (tape.recording || tape.playing)
+ DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
+
+ UpdateAndDisplayGameControlValues();
+}
+
void AdvanceFrameAndPlayerCounters(int player_nr)
{
int i;
if (!advance_player_counters) /* not all players may be affected */
continue;
-#if USE_NEW_PLAYER_ANIM
if (move_frames == 0) /* less than one move per game frame */
{
int stepsize = TILEX / move_delay_value;
if (count % delay == 0)
move_frames = 1;
}
-#endif
stored_player[i].Frame += move_frames;
if (stored_player[i].drop_delay > 0)
stored_player[i].drop_delay--;
+
+ if (stored_player[i].is_dropping_pressed)
+ stored_player[i].drop_pressed_delay++;
}
}
+void StartGameActions(boolean init_network_game, boolean record_tape,
+ int random_seed)
+{
+ unsigned int new_random_seed = InitRND(random_seed);
+
+ if (record_tape)
+ TapeStartRecording(new_random_seed);
+
+#if defined(NETWORK_AVALIABLE)
+ if (init_network_game)
+ {
+ SendToServer_StartPlaying();
+
+ return;
+ }
+#endif
+
+ InitGame();
+}
+
void GameActions()
{
- static unsigned long game_frame_delay = 0;
- unsigned long game_frame_delay_value;
- int magic_wall_x = 0, magic_wall_y = 0;
- int i, x, y, element, graphic;
+ static unsigned int game_frame_delay = 0;
+ unsigned int game_frame_delay_value;
byte *recorded_player_action;
byte summarized_player_action = 0;
byte tape_action[MAX_PLAYERS];
+ int i;
+
+ /* detect endless loops, caused by custom element programming */
+ if (recursion_loop_detected && recursion_loop_depth == 0)
+ {
+ char *message = getStringCat3("Internal Error! Element ",
+ EL_NAME(recursion_loop_element),
+ " caused endless loop! Quit the game?");
+
+ Error(ERR_WARN, "element '%s' caused endless loop in game engine",
+ EL_NAME(recursion_loop_element));
+
+ RequestQuitGameExt(FALSE, level_editor_test_game, message);
+
+ recursion_loop_detected = FALSE; /* if game should be continued */
+
+ free(message);
+
+ return;
+ }
+
+ if (game.restart_level)
+ StartGameActions(options.network, setup.autorecord, level.random_seed);
+
+ /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
+ if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+ {
+ if (level.native_em_level->lev->home == 0) /* all players at home */
+ {
+ PlayerWins(local_player);
+
+ AllPlayersGone = TRUE;
+
+ level.native_em_level->lev->home = -1;
+ }
- if (game_status != GAME_MODE_PLAYING)
+ if (level.native_em_level->ply[0]->alive == 0 &&
+ level.native_em_level->ply[1]->alive == 0 &&
+ level.native_em_level->ply[2]->alive == 0 &&
+ level.native_em_level->ply[3]->alive == 0) /* all dead */
+ AllPlayersGone = TRUE;
+ }
+ else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+ {
+ if (game_sp.LevelSolved &&
+ !game_sp.GameOver) /* game won */
+ {
+ PlayerWins(local_player);
+
+ game_sp.GameOver = TRUE;
+
+ AllPlayersGone = TRUE;
+ }
+
+ if (game_sp.GameOver) /* game lost */
+ AllPlayersGone = TRUE;
+ }
+
+ if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
+ GameWon();
+
+ if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
+ TapeStop();
+
+ if (game_status != GAME_MODE_PLAYING) /* status might have changed */
return;
game_frame_delay_value =
if (tape.playing && tape.warp_forward && !tape.pausing)
game_frame_delay_value = 0;
+#if 0
/* ---------- main game synchronization point ---------- */
- WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
+ int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
- InitPlayfieldScanModeVars();
+ printf("::: skip == %d\n", skip);
-#if USE_ONE_MORE_CHANGE_PER_FRAME
- if (game.engine_version >= VERSION_IDENT(3,2,0,7))
- {
- SCAN_PLAYFIELD(x, y)
- {
- ChangeCount[x][y] = 0;
- ChangeEvent[x][y] = -1;
- }
- }
+#else
+ /* ---------- main game synchronization point ---------- */
+
+ WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
#endif
if (network_playing && !network_player_action_received)
if (!network_player_action_received)
return; /* failed to get network player actions in time */
+
+ /* do not yet reset "network_player_action_received" (for tape.pausing) */
}
if (tape.pausing)
return;
+ /* at this point we know that we really continue executing the game */
+
+ network_player_action_received = FALSE;
+
+ /* when playing tape, read previously recorded player input from tape data */
recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
-#if 1
- /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
- if (recorded_player_action == NULL && tape.pausing)
+ /* TapePlayAction() may return NULL when toggling to "pause before death" */
+ if (tape.pausing)
return;
-#endif
+
+ if (tape.set_centered_player)
+ {
+ game.centered_player_nr_next = tape.centered_player_nr_next;
+ game.set_centered_player = TRUE;
+ }
for (i = 0; i < MAX_PLAYERS; i++)
{
summarized_player_action |= stored_player[i].action;
- if (!network_playing)
+ if (!network_playing && (game.team_mode || tape.playing))
stored_player[i].effective_action = stored_player[i].action;
}
SendToServer_MovePlayer(summarized_player_action);
#endif
- if (!options.network && !setup.team_mode)
- local_player->effective_action = summarized_player_action;
+ // summarize all actions at local players mapped input device position
+ // (this allows using different input devices in single player mode)
+ if (!options.network && !game.team_mode)
+ stored_player[map_player_action[local_player->index_nr]].effective_action =
+ summarized_player_action;
+
+ if (tape.recording &&
+ setup.team_mode &&
+ setup.input_on_focus &&
+ game.centered_player_nr != -1)
+ {
+ for (i = 0; i < MAX_PLAYERS; i++)
+ stored_player[i].effective_action =
+ (i == game.centered_player_nr ? summarized_player_action : 0);
+ }
if (recorded_player_action != NULL)
for (i = 0; i < MAX_PLAYERS; i++)
{
tape_action[i] = stored_player[i].effective_action;
- if (tape.recording && tape_action[i] && !tape.player_participates[i])
- tape.player_participates[i] = TRUE; /* player just appeared from CE */
+ /* (this may happen in the RND game engine if a player was not present on
+ the playfield on level start, but appeared later from a custom element */
+ if (setup.team_mode &&
+ tape.recording &&
+ tape_action[i] &&
+ !tape.player_participates[i])
+ tape.player_participates[i] = TRUE;
}
- /* only save actions from input devices, but not programmed actions */
+ /* only record actions from input devices, but not programmed actions */
if (tape.recording)
TapeRecordAction(tape_action);
+#if USE_NEW_PLAYER_ASSIGNMENTS
+ // !!! also map player actions in single player mode !!!
+ // if (game.team_mode)
+ if (1)
+ {
+ byte mapped_action[MAX_PLAYERS];
+
+#if DEBUG_PLAYER_ACTIONS
+ printf(":::");
+ for (i = 0; i < MAX_PLAYERS; i++)
+ printf(" %d, ", stored_player[i].effective_action);
+#endif
+
+ for (i = 0; i < MAX_PLAYERS; i++)
+ mapped_action[i] = stored_player[map_player_action[i]].effective_action;
+
+ for (i = 0; i < MAX_PLAYERS; i++)
+ stored_player[i].effective_action = mapped_action[i];
+
+#if DEBUG_PLAYER_ACTIONS
+ printf(" =>");
+ for (i = 0; i < MAX_PLAYERS; i++)
+ printf(" %d, ", stored_player[i].effective_action);
+ printf("\n");
+#endif
+ }
+#if DEBUG_PLAYER_ACTIONS
+ else
+ {
+ printf(":::");
+ for (i = 0; i < MAX_PLAYERS; i++)
+ printf(" %d, ", stored_player[i].effective_action);
+ printf("\n");
+ }
+#endif
+#endif
+
+ for (i = 0; i < MAX_PLAYERS; i++)
+ {
+ // allow engine snapshot in case of changed movement attempt
+ if ((game.snapshot.last_action[i] & KEY_MOTION) !=
+ (stored_player[i].effective_action & KEY_MOTION))
+ game.snapshot.changed_action = TRUE;
+
+ // allow engine snapshot in case of snapping/dropping attempt
+ if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
+ (stored_player[i].effective_action & KEY_BUTTON) != 0)
+ game.snapshot.changed_action = TRUE;
+
+ game.snapshot.last_action[i] = stored_player[i].effective_action;
+ }
+
+ if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+ {
+ GameActions_EM_Main();
+ }
+ else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+ {
+ GameActions_SP_Main();
+ }
+ else
+ {
+ GameActions_RND_Main();
+ }
+
+ BlitScreenToBitmap(backbuffer);
+
+ CheckLevelTime();
+
+ AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
+
+ if (options.debug) /* calculate frames per second */
+ {
+ static unsigned int fps_counter = 0;
+ static int fps_frames = 0;
+ unsigned int fps_delay_ms = Counter() - fps_counter;
+
+ fps_frames++;
+
+ if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
+ {
+ global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
+
+ fps_frames = 0;
+ fps_counter = Counter();
+ }
+
+ redraw_mask |= REDRAW_FPS;
+ }
+}
+
+void GameActions_EM_Main()
+{
+ byte effective_action[MAX_PLAYERS];
+ boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
+ int i;
+
+ for (i = 0; i < MAX_PLAYERS; i++)
+ effective_action[i] = stored_player[i].effective_action;
+
+ GameActions_EM(effective_action, warp_mode);
+}
+
+void GameActions_SP_Main()
+{
+ byte effective_action[MAX_PLAYERS];
+ boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
+ int i;
+
+ for (i = 0; i < MAX_PLAYERS; i++)
+ effective_action[i] = stored_player[i].effective_action;
+
+ GameActions_SP(effective_action, warp_mode);
+}
+
+void GameActions_RND_Main()
+{
+ GameActions_RND();
+}
+
+void GameActions_RND()
+{
+ int magic_wall_x = 0, magic_wall_y = 0;
+ int i, x, y, element, graphic;
+
+ InitPlayfieldScanModeVars();
+
+ if (game.engine_version >= VERSION_IDENT(3,2,0,7))
+ {
+ SCAN_PLAYFIELD(x, y)
+ {
+ ChangeCount[x][y] = 0;
+ ChangeEvent[x][y] = -1;
+ }
+ }
+
+ if (game.set_centered_player)
+ {
+ boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
+
+ /* switching to "all players" only possible if all players fit to screen */
+ if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
+ {
+ game.centered_player_nr_next = game.centered_player_nr;
+ game.set_centered_player = FALSE;
+ }
+
+ /* do not switch focus to non-existing (or non-active) player */
+ if (game.centered_player_nr_next >= 0 &&
+ !stored_player[game.centered_player_nr_next].active)
+ {
+ game.centered_player_nr_next = game.centered_player_nr;
+ game.set_centered_player = FALSE;
+ }
+ }
+
+ if (game.set_centered_player &&
+ ScreenMovPos == 0) /* screen currently aligned at tile position */
+ {
+ int sx, sy;
+
+ if (game.centered_player_nr_next == -1)
+ {
+ setScreenCenteredToAllPlayers(&sx, &sy);
+ }
+ else
+ {
+ sx = stored_player[game.centered_player_nr_next].jx;
+ sy = stored_player[game.centered_player_nr_next].jy;
+ }
+
+ game.centered_player_nr = game.centered_player_nr_next;
+ game.set_centered_player = FALSE;
+
+ DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
+ DrawGameDoorValues();
+ }
+
for (i = 0; i < MAX_PLAYERS; i++)
{
int actual_player_action = stored_player[i].effective_action;
if (stored_player[i].programmed_action)
actual_player_action = stored_player[i].programmed_action;
-#if 1
PlayerActions(&stored_player[i], actual_player_action);
-#else
- tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
-
- if (tape.recording && tape_action[i] && !tape.player_participates[i])
- tape.player_participates[i] = TRUE; /* player just appeared from CE */
-#endif
ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
}
- network_player_action_received = FALSE;
-
ScrollScreen(NULL, SCROLL_GO_ON);
/* for backwards compatibility, the following code emulates a fixed bug that
/* continue moving after pushing (this is actually a bug) */
if (!IS_MOVING(x, y))
- {
Stop[x][y] = FALSE;
- }
}
}
}
-#if 1
SCAN_PLAYFIELD(x, y)
-#else
- for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
-#endif
{
ChangeCount[x][y] = 0;
ChangeEvent[x][y] = -1;
RemoveField(x, y);
}
-#if USE_NEW_SNAP_DELAY
if (Feld[x][y] == EL_ELEMENT_SNAPPING)
{
MovDelay[x][y]--;
if (MovDelay[x][y] <= 0)
{
RemoveField(x, y);
- DrawLevelField(x, y);
+ TEST_DrawLevelField(x, y);
TestIfElementTouchesCustomElement(x, y); /* for empty space */
}
}
-#endif
#if DEBUG
if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
WasJustFalling[x][y]--;
if (CheckCollision[x][y] > 0)
CheckCollision[x][y]--;
+ if (CheckImpact[x][y] > 0)
+ CheckImpact[x][y]--;
GfxFrame[x][y]++;
if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
{
ResetGfxAnimation(x, y);
- DrawLevelField(x, y);
+ TEST_DrawLevelField(x, y);
}
#if DEBUG
#endif
}
-#if 1
SCAN_PLAYFIELD(x, y)
-#else
- for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
-#endif
{
element = Feld[x][y];
graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
-#if 0
- printf("::: %d,%d\n", x, y);
-
- if (element == EL_ROCK)
- printf("::: Yo man! Rocks can fall!\n");
-#endif
-
- if (graphic_info[graphic].anim_global_sync)
- GfxFrame[x][y] = FrameCounter;
- else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
- {
- int old_gfx_frame = GfxFrame[x][y];
-
- GfxFrame[x][y] = CustomValue[x][y];
-
-#if 1
- if (GfxFrame[x][y] != old_gfx_frame)
-#endif
- DrawLevelGraphicAnimation(x, y, graphic);
- }
- else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
- {
- int old_gfx_frame = GfxFrame[x][y];
-
- GfxFrame[x][y] = element_info[element].collect_score;
-
-#if 1
- if (GfxFrame[x][y] != old_gfx_frame)
-#endif
- DrawLevelGraphicAnimation(x, y, graphic);
- }
+ ResetGfxFrame(x, y, TRUE);
if (ANIM_MODE(graphic) == ANIM_RANDOM &&
IS_NEXT_FRAME(GfxFrame[x][y], graphic))
(game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
{
int page = element_info[element].event_page_nr[CE_DELAY];
-#if 0
- HandleElementChange(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
-#else
-
-#if 0
- printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
-#endif
-
-#if 0
- if (element == EL_CUSTOM_255)
- printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
-#endif
-#if 1
HandleElementChange(x, y, page);
-#else
- if (CAN_CHANGE(element))
- HandleElementChange(x, y, page);
-
- if (HAS_ACTION(element))
- ExecuteCustomElementAction(x, y, element, page);
-#endif
-
-#endif
element = Feld[x][y];
graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
if (IS_GEM(element) || element == EL_SP_INFOTRON)
- EdelsteinFunkeln(x, y);
+ TEST_DrawTwinkleOnField(x, y);
}
else if ((element == EL_ACID ||
element == EL_EXIT_OPEN ||
+ element == EL_EM_EXIT_OPEN ||
element == EL_SP_EXIT_OPEN ||
+ element == EL_STEEL_EXIT_OPEN ||
+ element == EL_EM_STEEL_EXIT_OPEN ||
element == EL_SP_TERMINAL ||
element == EL_SP_TERMINAL_ACTIVE ||
element == EL_EXTRA_TIME ||
Life(x, y);
else if (element == EL_EXIT_CLOSED)
CheckExit(x, y);
+ else if (element == EL_EM_EXIT_CLOSED)
+ CheckExitEM(x, y);
+ else if (element == EL_STEEL_EXIT_CLOSED)
+ CheckExitSteel(x, y);
+ else if (element == EL_EM_STEEL_EXIT_CLOSED)
+ CheckExitSteelEM(x, y);
else if (element == EL_SP_EXIT_CLOSED)
CheckExitSP(x, y);
- else if (element == EL_EXPANDABLE_WALL_GROWING)
+ else if (element == EL_EXPANDABLE_WALL_GROWING ||
+ element == EL_EXPANDABLE_STEELWALL_GROWING)
MauerWaechst(x, y);
else if (element == EL_EXPANDABLE_WALL ||
element == EL_EXPANDABLE_WALL_HORIZONTAL ||
element == EL_EXPANDABLE_WALL_VERTICAL ||
- element == EL_EXPANDABLE_WALL_ANY)
+ element == EL_EXPANDABLE_WALL_ANY ||
+ element == EL_BD_EXPANDABLE_WALL)
MauerAbleger(x, y);
+ else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
+ element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
+ element == EL_EXPANDABLE_STEELWALL_ANY)
+ MauerAblegerStahl(x, y);
else if (element == EL_FLAMES)
CheckForDragon(x, y);
else if (element == EL_EXPLOSION)
element == EL_DIAGONAL_SHRINKING ||
element == EL_DIAGONAL_GROWING)
{
-#if 1
graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
-#endif
}
else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
-#if 0
- if (element == EL_CUSTOM_255 ||
- element == EL_CUSTOM_256)
- DrawLevelGraphicAnimation(x, y, graphic);
-#endif
-
if (IS_BELT_ACTIVE(element))
PlayLevelSoundAction(x, y, ACTION_ACTIVE);
element == EL_MAGIC_WALL_EMPTYING ||
element == EL_BD_MAGIC_WALL_FULL ||
element == EL_BD_MAGIC_WALL_ACTIVE ||
- element == EL_BD_MAGIC_WALL_EMPTYING) &&
+ element == EL_BD_MAGIC_WALL_EMPTYING ||
+ element == EL_DC_MAGIC_WALL_FULL ||
+ element == EL_DC_MAGIC_WALL_ACTIVE ||
+ element == EL_DC_MAGIC_WALL_EMPTYING) &&
ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
{
magic_wall_x = x;
/* new experimental amoeba growth stuff */
if (!(FrameCounter % 8))
{
- static unsigned long random = 1684108901;
+ static unsigned int random = 1684108901;
for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
{
(element == EL_EMPTY ||
CAN_GROW_INTO(element) ||
element == EL_QUICKSAND_EMPTY ||
+ element == EL_QUICKSAND_FAST_EMPTY ||
element == EL_ACID_SPLASH_LEFT ||
element == EL_ACID_SPLASH_RIGHT))
{
}
#endif
-#if 0
- if (game.explosions_delayed)
-#endif
- {
- game.explosions_delayed = FALSE;
-
-#if 1
- SCAN_PLAYFIELD(x, y)
-#else
- for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
-#endif
- {
- element = Feld[x][y];
+ game.explosions_delayed = FALSE;
- if (ExplodeField[x][y])
- Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
- else if (element == EL_EXPLOSION)
- Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
+ SCAN_PLAYFIELD(x, y)
+ {
+ element = Feld[x][y];
- ExplodeField[x][y] = EX_TYPE_NONE;
- }
+ if (ExplodeField[x][y])
+ Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
+ else if (element == EL_EXPLOSION)
+ Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
- game.explosions_delayed = TRUE;
+ ExplodeField[x][y] = EX_TYPE_NONE;
}
+ game.explosions_delayed = TRUE;
+
if (game.magic_wall_active)
{
if (!(game.magic_wall_time_left % 4))
element == EL_BD_MAGIC_WALL_ACTIVE ||
element == EL_BD_MAGIC_WALL_EMPTYING)
PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
+ else if (element == EL_DC_MAGIC_WALL_FULL ||
+ element == EL_DC_MAGIC_WALL_ACTIVE ||
+ element == EL_DC_MAGIC_WALL_EMPTYING)
+ PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
else
PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
}
if (game.magic_wall_time_left > 0)
{
game.magic_wall_time_left--;
+
if (!game.magic_wall_time_left)
{
-#if 1
SCAN_PLAYFIELD(x, y)
-#else
- for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
-#endif
{
element = Feld[x][y];
element == EL_MAGIC_WALL_FULL)
{
Feld[x][y] = EL_MAGIC_WALL_DEAD;
- DrawLevelField(x, y);
+ TEST_DrawLevelField(x, y);
}
else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
element == EL_BD_MAGIC_WALL_FULL)
{
Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
- DrawLevelField(x, y);
+ TEST_DrawLevelField(x, y);
+ }
+ else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
+ element == EL_DC_MAGIC_WALL_FULL)
+ {
+ Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
+ TEST_DrawLevelField(x, y);
}
}
}
}
- if (TimeFrames >= FRAMES_PER_SECOND)
+#if USE_DELAYED_GFX_REDRAW
+ SCAN_PLAYFIELD(x, y)
{
- TimeFrames = 0;
- TapeTime++;
-
- for (i = 0; i < MAX_PLAYERS; i++)
- {
- struct PlayerInfo *player = &stored_player[i];
-
- if (SHIELD_ON(player))
- {
- player->shield_normal_time_left--;
-
- if (player->shield_deadly_time_left > 0)
- player->shield_deadly_time_left--;
- }
- }
-
- if (!level.use_step_counter)
+ if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
{
- TimePlayed++;
+ /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
+ !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
- if (TimeLeft > 0)
- {
- TimeLeft--;
+ if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
+ DrawLevelField(x, y);
- if (TimeLeft <= 10 && setup.time_limit)
- PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
+ if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
+ DrawLevelFieldCrumbled(x, y);
- DrawGameValue_Time(TimeLeft);
+ if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
+ DrawLevelFieldCrumbledNeighbours(x, y);
- if (!TimeLeft && setup.time_limit)
- for (i = 0; i < MAX_PLAYERS; i++)
- KillPlayer(&stored_player[i]);
- }
- else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
- DrawGameValue_Time(TimePlayed);
+ if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
+ DrawTwinkleOnField(x, y);
}
- if (tape.recording || tape.playing)
- DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
+ GfxRedraw[x][y] = GFX_REDRAW_NONE;
}
+#endif
DrawAllPlayers();
PlayAllPlayersSound();
- if (options.debug) /* calculate frames per second */
- {
- static unsigned long fps_counter = 0;
- static int fps_frames = 0;
- unsigned long fps_delay_ms = Counter() - fps_counter;
-
- fps_frames++;
-
- if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
- {
- global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
-
- fps_frames = 0;
- fps_counter = Counter();
- }
-
- redraw_mask |= REDRAW_FPS;
- }
-
- AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
-
if (local_player->show_envelope != 0 && local_player->MovPos == 0)
{
ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
void ScrollLevel(int dx, int dy)
{
- int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
+ int scroll_offset = 2 * TILEX_VAR;
int x, y;
BlitBitmap(drawto_field, drawto_field,
- FX + TILEX * (dx == -1) - softscroll_offset,
- FY + TILEY * (dy == -1) - softscroll_offset,
- SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
- SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
- FX + TILEX * (dx == 1) - softscroll_offset,
- FY + TILEY * (dy == 1) - softscroll_offset);
+ FX + TILEX_VAR * (dx == -1) - scroll_offset,
+ FY + TILEY_VAR * (dy == -1) - scroll_offset,
+ SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
+ SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
+ FX + TILEX_VAR * (dx == 1) - scroll_offset,
+ FY + TILEY_VAR * (dy == 1) - scroll_offset);
- if (dx)
+ if (dx != 0)
{
x = (dx == 1 ? BX1 : BX2);
for (y = BY1; y <= BY2; y++)
DrawScreenField(x, y);
}
- if (dy)
+ if (dy != 0)
{
y = (dy == 1 ? BY1 : BY2);
for (x = BX1; x <= BX2; x++)
static void CheckGravityMovement(struct PlayerInfo *player)
{
- if (game.gravity && !player->programmed_action)
+ if (player->gravity && !player->programmed_action)
{
int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
int move_dir_vertical = player->effective_action & MV_VERTICAL;
- boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
+ boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
int jx = player->jx, jy = player->jy;
boolean player_is_moving_to_valid_field =
(!player_is_snapping &&
{
return CheckGravityMovement(player);
- if (game.gravity && !player->programmed_action)
+ if (player->gravity && !player->programmed_action)
{
int jx = player->jx, jy = player->jy;
boolean field_under_player_is_free =
{
int jx = player->jx, jy = player->jy;
int new_jx = jx + dx, new_jy = jy + dy;
-#if !USE_FIXED_DONT_RUN_INTO
- int element;
-#endif
int can_move;
boolean player_can_move = !player->cannot_move;
if (!player_can_move)
{
-#if 1
if (player->MovPos == 0)
{
player->is_moving = FALSE;
player->is_snapping = FALSE;
player->is_pushing = FALSE;
}
-#else
- DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
- SnapField(player, 0, 0);
-#endif
-
-#if 0
- return MP_NO_ACTION;
-#endif
}
- if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
+ if (!options.network && game.centered_player_nr == -1 &&
+ !AllPlayersInSight(player, new_jx, new_jy))
return MP_NO_ACTION;
-#if !USE_FIXED_DONT_RUN_INTO
- element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
-
- /* (moved to DigField()) */
- if (player_can_move && DONT_RUN_INTO(element))
- {
- if (element == EL_ACID && dx == 0 && dy == 1)
- {
- SplashAcid(new_jx, new_jy);
- Feld[jx][jy] = EL_PLAYER_1;
- InitMovingField(jx, jy, MV_DOWN);
- Store[jx][jy] = EL_ACID;
- ContinueMoving(jx, jy);
- BuryPlayer(player);
- }
- else
- TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
-
- return MP_MOVING;
- }
-#endif
-
can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
-#if 0
-#if USE_FIXED_DONT_RUN_INTO
- if (can_move == MP_DONT_RUN_INTO)
- return MP_MOVING;
-#endif
-#endif
if (can_move != MP_MOVING)
return can_move;
-#if USE_FIXED_DONT_RUN_INTO
-#endif
-
/* check if DigField() has caused relocation of the player */
if (player->jx != jx || player->jy != jy)
return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
PlayerVisit[jx][jy] = FrameCounter;
+ player->is_moving = TRUE;
+
+#if 1
+ /* should better be called in MovePlayer(), but this breaks some tapes */
ScrollPlayer(player, SCROLL_INIT);
+#endif
return MP_MOVING;
}
int original_move_delay_value = player->move_delay_value;
#if DEBUG
- printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
+ printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
tape.counter);
#endif
player->move_delay_value = original_move_delay_value;
}
+ player->is_active = FALSE;
+
if (player->last_move_dir & MV_HORIZONTAL)
{
if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
}
+ if (!moved && !player->is_active)
+ {
+ player->is_moving = FALSE;
+ player->is_digging = FALSE;
+ player->is_collecting = FALSE;
+ player->is_snapping = FALSE;
+ player->is_pushing = FALSE;
+ }
+
jx = player->jx;
jy = player->jy;
if (moved & MP_MOVING && !ScreenMovPos &&
- (player == local_player || !options.network))
+ (player->index_nr == game.centered_player_nr ||
+ game.centered_player_nr == -1))
{
int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
- int offset = (setup.scroll_delay ? 3 : 0);
+ int offset = game.scroll_delay_value;
if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
{
if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
{
- if (!options.network && !AllPlayersInVisibleScreen())
+ if (!options.network && game.centered_player_nr == -1 &&
+ !AllPlayersInVisibleScreen())
{
scroll_x = old_scroll_x;
scroll_y = old_scroll_y;
else if (old_jx == jx && old_jy != jy)
player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
- DrawLevelField(jx, jy); /* for "crumbled sand" */
+ TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
player->last_move_dir = player->MovDir;
player->is_moving = TRUE;
player->is_snapping = FALSE;
player->is_switching = FALSE;
player->is_dropping = FALSE;
+ player->is_dropping_pressed = FALSE;
+ player->drop_pressed_delay = 0;
+
+#if 0
+ /* should better be called here than above, but this breaks some tapes */
+ ScrollPlayer(player, SCROLL_INIT);
+#endif
}
else
{
int last_jx = player->last_jx, last_jy = player->last_jy;
int move_stepsize = TILEX / player->move_delay_value;
-#if USE_NEW_PLAYER_SPEED
if (!player->active)
return;
if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
return;
-#else
- if (!player->active || player->MovPos == 0)
- return;
-#endif
if (mode == SCROLL_INIT)
{
last_field_block_delay += player->move_delay_value;
/* when blocking enabled, prevent moving up despite gravity */
- if (game.gravity && player->MovDir == MV_UP)
+ if (player->gravity && player->MovDir == MV_UP)
block_delay_adjustment = -1;
}
MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
}
-#if USE_NEW_PLAYER_SPEED
if (player->MovPos != 0) /* player has not yet reached destination */
return;
-#else
- return;
-#endif
}
else if (!FrameReached(&player->actual_frame_counter, 1))
return;
-#if 0
- printf("::: player->MovPos: %d -> %d\n",
- player->MovPos,
- player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
-#endif
-
-#if USE_NEW_PLAYER_SPEED
if (player->MovPos != 0)
{
player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
if (player->MovPos == 0)
CheckGravityMovement(player);
}
-#else
- player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
- player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
-
- /* before DrawPlayer() to draw correct player graphic for this case */
- if (player->MovPos == 0)
- CheckGravityMovement(player);
-#endif
if (player->MovPos == 0) /* player reached destination field */
{
-#if 0
- printf("::: player reached destination field\n");
-#endif
-
if (player->move_delay_reset_counter > 0)
{
player->move_delay_reset_counter--;
player->last_jy = jy;
if (Feld[jx][jy] == EL_EXIT_OPEN ||
+ Feld[jx][jy] == EL_EM_EXIT_OPEN ||
+ Feld[jx][jy] == EL_EM_EXIT_OPENING ||
+ Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
+ Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
+ Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
Feld[jx][jy] == EL_SP_EXIT_OPEN ||
Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
{
if (local_player->friends_still_needed == 0 ||
IS_SP_ELEMENT(Feld[jx][jy]))
- player->LevelSolved = player->GameOver = TRUE;
+ PlayerWins(player);
}
/* this breaks one level: "machine", level 000 */
CE_PLAYER_ENTERS_X,
player->index_bit, enter_side);
- CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
+ CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
CE_MOVE_OF_X, move_direction);
}
RemovePlayer(player);
}
- if (level.use_step_counter)
+ if (!local_player->LevelSolved && level.use_step_counter)
{
int i;
TimeLeft--;
if (TimeLeft <= 10 && setup.time_limit)
- PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
+ PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
+
+ game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
- DrawGameValue_Time(TimeLeft);
+ DisplayGameControlValues();
if (!TimeLeft && setup.time_limit)
for (i = 0; i < MAX_PLAYERS; i++)
KillPlayer(&stored_player[i]);
}
- else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
- DrawGameValue_Time(TimePlayed);
+ else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
+ {
+ game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
+
+ DisplayGameControlValues();
+ }
}
if (tape.single_step && tape.recording && !tape.pausing &&
!player->programmed_action)
TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
+
+ if (!player->programmed_action)
+ CheckSaveEngineSnapshot(player);
}
}
void ScrollScreen(struct PlayerInfo *player, int mode)
{
- static unsigned long screen_frame_counter = 0;
+ static unsigned int screen_frame_counter = 0;
if (mode == SCROLL_INIT)
{
if (!IN_LEV_FIELD(xx, yy))
continue;
- if (IS_PLAYER(x, y))
+ if (IS_PLAYER(x, y)) /* player found at center element */
{
struct PlayerInfo *player = PLAYERINFO(x, y);
CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
CE_PLAYER_TOUCHES_X,
player->index_bit, border_side);
+
+ {
+ /* use player element that is initially defined in the level playfield,
+ not the player element that corresponds to the runtime player number
+ (example: a level that contains EL_PLAYER_3 as the only player would
+ incorrectly give EL_PLAYER_1 for "player->element_nr") */
+ int player_element = PLAYERINFO(x, y)->initial_element;
+
+ CheckElementChangeBySide(xx, yy, border_element, player_element,
+ CE_TOUCHING_X, border_side);
+ }
}
- else if (IS_PLAYER(xx, yy))
+ else if (IS_PLAYER(xx, yy)) /* player found at border element */
{
struct PlayerInfo *player = PLAYERINFO(xx, yy);
CheckTriggeredElementChangeByPlayer(x, y, center_element,
CE_PLAYER_TOUCHES_X,
player->index_bit, center_side);
+
+ {
+ /* use player element that is initially defined in the level playfield,
+ not the player element that corresponds to the runtime player number
+ (example: a level that contains EL_PLAYER_3 as the only player would
+ incorrectly give EL_PLAYER_1 for "player->element_nr") */
+ int player_element = PLAYERINFO(xx, yy)->initial_element;
+
+ CheckElementChangeBySide(x, y, center_element, player_element,
+ CE_TOUCHING_X, center_side);
+ }
+
break;
}
}
};
boolean change_center_element = FALSE;
int center_element = Feld[x][y]; /* should always be non-moving! */
+ int border_element_old[NUM_DIRECTIONS];
int i;
for (i = 0; i < NUM_DIRECTIONS; i++)
{
int xx = x + xy[i][0];
int yy = y + xy[i][1];
- int center_side = trigger_sides[i][0];
- int border_side = trigger_sides[i][1];
int border_element;
+ border_element_old[i] = -1;
+
if (!IN_LEV_FIELD(xx, yy))
continue;
else
continue; /* center and border element do not touch */
+ border_element_old[i] = border_element;
+ }
+
+ for (i = 0; i < NUM_DIRECTIONS; i++)
+ {
+ int xx = x + xy[i][0];
+ int yy = y + xy[i][1];
+ int center_side = trigger_sides[i][0];
+ int border_element = border_element_old[i];
+
+ if (border_element == -1)
+ continue;
+
+ /* check for change of border element */
+ CheckElementChangeBySide(xx, yy, border_element, center_element,
+ CE_TOUCHING_X, center_side);
+
+ /* (center element cannot be player, so we dont have to check this here) */
+ }
+
+ for (i = 0; i < NUM_DIRECTIONS; i++)
+ {
+ int xx = x + xy[i][0];
+ int yy = y + xy[i][1];
+ int border_side = trigger_sides[i][1];
+ int border_element = border_element_old[i];
+
+ if (border_element == -1)
+ continue;
+
/* check for change of center element (but change it only once) */
if (!change_center_element)
change_center_element =
CheckElementChangeBySide(x, y, center_element, border_element,
CE_TOUCHING_X, border_side);
- /* check for change of border element */
- CheckElementChangeBySide(xx, yy, border_element, center_element,
- CE_TOUCHING_X, center_side);
+ if (IS_PLAYER(xx, yy))
+ {
+ /* use player element that is initially defined in the level playfield,
+ not the player element that corresponds to the runtime player number
+ (example: a level that contains EL_PLAYER_3 as the only player would
+ incorrectly give EL_PLAYER_1 for "player->element_nr") */
+ int player_element = PLAYERINFO(xx, yy)->initial_element;
+
+ CheckElementChangeBySide(x, y, center_element, player_element,
+ CE_TOUCHING_X, border_side);
+ }
}
}
CheckElementChangeBySide(x, y, hitting_element, touched_element,
CE_HITTING_X, touched_side);
- CheckElementChangeBySide(hitx, hity, touched_element,
- hitting_element, CE_HIT_BY_X, hitting_side);
+ CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
+ CE_HIT_BY_X, hitting_side);
CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
CE_HIT_BY_SOMETHING, opposite_direction);
+
+ if (IS_PLAYER(hitx, hity))
+ {
+ /* use player element that is initially defined in the level playfield,
+ not the player element that corresponds to the runtime player number
+ (example: a level that contains EL_PLAYER_3 as the only player would
+ incorrectly give EL_PLAYER_1 for "player->element_nr") */
+ int player_element = PLAYERINFO(hitx, hity)->initial_element;
+
+ CheckElementChangeBySide(x, y, hitting_element, player_element,
+ CE_HITTING_X, touched_side);
+ }
}
}
CE_HITTING_SOMETHING, direction);
}
-#if 0
-void TestIfElementSmashesCustomElement(int x, int y, int direction)
-{
- int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
- int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
- int hitx = x + dx, hity = y + dy;
- int hitting_element = Feld[x][y];
- int touched_element;
-#if 0
- boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
- !IS_FREE(hitx, hity) &&
- (!IS_MOVING(hitx, hity) ||
- MovDir[hitx][hity] != direction ||
- ABS(MovPos[hitx][hity]) <= TILEY / 2));
-#endif
-
- if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
- return;
-
-#if 0
- if (IN_LEV_FIELD(hitx, hity) && !object_hit)
- return;
-#endif
-
- touched_element = (IN_LEV_FIELD(hitx, hity) ?
- MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
-
- CheckElementChangeBySide(x, y, hitting_element, touched_element,
- EP_CAN_SMASH_EVERYTHING, direction);
-
- if (IN_LEV_FIELD(hitx, hity))
- {
- int opposite_direction = MV_DIR_OPPOSITE(direction);
- int hitting_side = direction;
- int touched_side = opposite_direction;
-#if 0
- int touched_element = MovingOrBlocked2Element(hitx, hity);
-#endif
-#if 1
- boolean object_hit = (!IS_MOVING(hitx, hity) ||
- MovDir[hitx][hity] != direction ||
- ABS(MovPos[hitx][hity]) <= TILEY / 2);
-
- object_hit = TRUE;
-#endif
-
- if (object_hit)
- {
- int i;
-
- CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
- CE_SMASHED_BY_SOMETHING, opposite_direction);
-
- CheckElementChangeBySide(x, y, hitting_element, touched_element,
- CE_OTHER_IS_SMASHING, touched_side);
-
- CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
- CE_OTHER_GETS_SMASHED, hitting_side);
- }
- }
-}
-#endif
-
void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
{
int i, kill_x = -1, kill_y = -1;
test_x = bad_x + test_xy[i][0];
test_y = bad_y + test_xy[i][1];
+
if (!IN_LEV_FIELD(test_x, test_y))
continue;
kill_x = test_x;
kill_y = test_y;
+
break;
}
else if (test_element == EL_PENGUIN)
{
kill_x = test_x;
kill_y = test_y;
+
break;
}
}
}
}
+void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
+{
+ int bad_element = Feld[bad_x][bad_y];
+ int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
+ int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
+ int test_x = bad_x + dx, test_y = bad_y + dy;
+ int test_move_dir, test_element;
+ int kill_x = -1, kill_y = -1;
+
+ if (!IN_LEV_FIELD(test_x, test_y))
+ return;
+
+ test_move_dir =
+ (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
+
+ test_element = Feld[test_x][test_y];
+
+ if (test_move_dir != bad_move_dir)
+ {
+ /* good thing can be player or penguin that does not move away */
+ if (IS_PLAYER(test_x, test_y))
+ {
+ struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
+
+ /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
+ player as being hit when he is moving towards the bad thing, because
+ the "get hit by" condition would be lost after the player stops) */
+ if (player->MovPos != 0 && player->MovDir == bad_move_dir)
+ return; /* player moves away from bad thing */
+
+ kill_x = test_x;
+ kill_y = test_y;
+ }
+ else if (test_element == EL_PENGUIN)
+ {
+ kill_x = test_x;
+ kill_y = test_y;
+ }
+ }
+
+ if (kill_x != -1 || kill_y != -1)
+ {
+ if (IS_PLAYER(kill_x, kill_y))
+ {
+ struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
+
+ if (player->shield_deadly_time_left > 0 &&
+ !IS_INDESTRUCTIBLE(bad_element))
+ Bang(bad_x, bad_y);
+ else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
+ KillPlayer(player);
+ }
+ else
+ Bang(kill_x, kill_y);
+ }
+}
+
void TestIfPlayerTouchesBadThing(int x, int y)
{
TestIfGoodThingHitsBadThing(x, y, MV_NONE);
if (!player->active)
return;
+#if 0
+ printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
+ player->killed, player->active, player->reanimated);
+#endif
+
+ /* the following code was introduced to prevent an infinite loop when calling
+ -> Bang()
+ -> CheckTriggeredElementChangeExt()
+ -> ExecuteCustomElementAction()
+ -> KillPlayer()
+ -> (infinitely repeating the above sequence of function calls)
+ which occurs when killing the player while having a CE with the setting
+ "kill player X when explosion of <player X>"; the solution using a new
+ field "player->killed" was chosen for backwards compatibility, although
+ clever use of the fields "player->active" etc. would probably also work */
+#if 1
+ if (player->killed)
+ return;
+#endif
+
+ player->killed = TRUE;
+
/* remove accessible field at the player's position */
Feld[jx][jy] = EL_EMPTY;
player->shield_normal_time_left = 0;
player->shield_deadly_time_left = 0;
+#if 0
+ printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
+ player->killed, player->active, player->reanimated);
+#endif
+
Bang(jx, jy);
- BuryPlayer(player);
+
+#if 0
+ printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
+ player->killed, player->active, player->reanimated);
+#endif
+
+ if (player->reanimated) /* killed player may have been reanimated */
+ player->killed = player->reanimated = FALSE;
+ else
+ BuryPlayer(player);
}
static void KillPlayerUnlessEnemyProtected(int x, int y)
StorePlayer[jx][jy] = 0;
if (player->is_moving)
- DrawLevelField(player->last_jx, player->last_jy);
+ TEST_DrawLevelField(player->last_jx, player->last_jy);
for (i = 0; i < MAX_PLAYERS; i++)
if (stored_player[i].active)
ExitY = ZY = jy;
}
-#if USE_NEW_SNAP_DELAY
static void setFieldForSnapping(int x, int y, int element, int direction)
{
struct ElementInfo *ei = &element_info[element];
GfxDir[x][y] = direction;
GfxFrame[x][y] = -1;
}
-#endif
/*
=============================================================================
=============================================================================
*/
-int DigField(struct PlayerInfo *player,
- int oldx, int oldy, int x, int y,
- int real_dx, int real_dy, int mode)
+static int DigField(struct PlayerInfo *player,
+ int oldx, int oldy, int x, int y,
+ int real_dx, int real_dy, int mode)
{
boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
boolean player_was_pushing = player->is_pushing;
int opposite_direction = MV_DIR_OPPOSITE(move_direction);
int dig_side = MV_DIR_OPPOSITE(move_direction);
int old_element = Feld[jx][jy];
-#if USE_FIXED_DONT_RUN_INTO
int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
-#else
- int element;
-#endif
int collect_count;
if (is_player) /* function can also be called by EL_PENGUIN */
}
}
-#if !USE_FIXED_DONT_RUN_INTO
- if (IS_MOVING(x, y) || IS_PLAYER(x, y))
- return MP_NO_ACTION;
-#endif
-
if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
old_element = Back[jx][jy];
game.engine_version >= VERSION_IDENT(2,2,0,0))
old_element = Back[jx][jy];
-#if 0
-#if USE_FIXED_DONT_RUN_INTO
- if (player_can_move && DONT_RUN_INTO(element))
- {
- if (element == EL_ACID && dx == 0 && dy == 1)
- {
- SplashAcid(x, y);
- Feld[jx][jy] = EL_PLAYER_1;
- InitMovingField(jx, jy, MV_DOWN);
- Store[jx][jy] = EL_ACID;
- ContinueMoving(jx, jy);
- BuryPlayer(player);
- }
- else
- TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
-
- return MP_DONT_RUN_INTO;
- }
-#endif
-#endif
-
-#if 1
-#if USE_FIXED_DONT_RUN_INTO
- if (player_can_move && DONT_RUN_INTO(element))
- {
- TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
-
- return MP_DONT_RUN_INTO;
- }
-#endif
-#endif
-
if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
return MP_NO_ACTION; /* field has no opening in this direction */
if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
return MP_NO_ACTION; /* field has no opening in this direction */
-#if 1
-#if USE_FIXED_DONT_RUN_INTO
if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
{
SplashAcid(x, y);
- Feld[jx][jy] = EL_PLAYER_1;
+
+ Feld[jx][jy] = player->artwork_element;
InitMovingField(jx, jy, MV_DOWN);
Store[jx][jy] = EL_ACID;
ContinueMoving(jx, jy);
return MP_DONT_RUN_INTO;
}
-#endif
-#endif
-#if 0
-#if USE_FIXED_DONT_RUN_INTO
if (player_can_move && DONT_RUN_INTO(element))
{
- if (element == EL_ACID && dx == 0 && dy == 1)
- {
- SplashAcid(x, y);
- Feld[jx][jy] = EL_PLAYER_1;
- InitMovingField(jx, jy, MV_DOWN);
- Store[jx][jy] = EL_ACID;
- ContinueMoving(jx, jy);
- BuryPlayer(player);
- }
- else
- TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
+ TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
return MP_DONT_RUN_INTO;
}
-#endif
-#endif
-#if USE_FIXED_DONT_RUN_INTO
if (IS_MOVING(x, y) || IS_PLAYER(x, y))
return MP_NO_ACTION;
-#endif
-
-#if !USE_FIXED_DONT_RUN_INTO
- element = Feld[x][y];
-#endif
collect_count = element_info[element].collect_count_initial;
CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
player->index_bit, dig_side);
+ if (element == EL_DC_LANDMINE)
+ Bang(x, y);
+
if (Feld[x][y] != element) /* field changed by snapping */
return MP_ACTION;
return MP_NO_ACTION;
}
- if (game.gravity && is_player && !player->is_auto_moving &&
+ if (player->gravity && is_player && !player->is_auto_moving &&
canFallDown(player) && move_direction != MV_DOWN &&
!canMoveToValidFieldWithGravity(jx, jy, move_direction))
return MP_NO_ACTION; /* player cannot walk here due to gravity */
return MP_NO_ACTION;
}
else if (element == EL_EXIT_OPEN ||
+ element == EL_EM_EXIT_OPEN ||
+ element == EL_EM_EXIT_OPENING ||
+ element == EL_STEEL_EXIT_OPEN ||
+ element == EL_EM_STEEL_EXIT_OPEN ||
+ element == EL_EM_STEEL_EXIT_OPENING ||
element == EL_SP_EXIT_OPEN ||
element == EL_SP_EXIT_OPENING)
{
if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
return MP_NO_ACTION;
}
- else if (IS_SP_PORT(element))
+ else if (IS_EMC_GATE(element))
{
- if (element == EL_SP_GRAVITY_PORT_LEFT ||
- element == EL_SP_GRAVITY_PORT_RIGHT ||
- element == EL_SP_GRAVITY_PORT_UP ||
- element == EL_SP_GRAVITY_PORT_DOWN)
- game.gravity = !game.gravity;
+ if (!player->key[EMC_GATE_NR(element)])
+ return MP_NO_ACTION;
+ }
+ else if (IS_EMC_GATE_GRAY(element))
+ {
+ if (!player->key[EMC_GATE_GRAY_NR(element)])
+ return MP_NO_ACTION;
+ }
+ else if (IS_EMC_GATE_GRAY_ACTIVE(element))
+ {
+ if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
+ return MP_NO_ACTION;
+ }
+ else if (element == EL_DC_GATE_WHITE ||
+ element == EL_DC_GATE_WHITE_GRAY ||
+ element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
+ {
+ if (player->num_white_keys == 0)
+ return MP_NO_ACTION;
+
+ player->num_white_keys--;
+ }
+ else if (IS_SP_PORT(element))
+ {
+ if (element == EL_SP_GRAVITY_PORT_LEFT ||
+ element == EL_SP_GRAVITY_PORT_RIGHT ||
+ element == EL_SP_GRAVITY_PORT_UP ||
+ element == EL_SP_GRAVITY_PORT_DOWN)
+ player->gravity = !player->gravity;
else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
element == EL_SP_GRAVITY_ON_PORT_UP ||
element == EL_SP_GRAVITY_ON_PORT_DOWN)
- game.gravity = TRUE;
+ player->gravity = TRUE;
else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
element == EL_SP_GRAVITY_OFF_PORT_UP ||
element == EL_SP_GRAVITY_OFF_PORT_DOWN)
- game.gravity = FALSE;
+ player->gravity = FALSE;
}
/* automatically move to the next field with double speed */
if (mode == DF_SNAP)
{
-#if USE_NEW_SNAP_DELAY
if (level.block_snap_field)
setFieldForSnapping(x, y, element, move_direction);
else
TestIfElementTouchesCustomElement(x, y); /* for empty space */
-#else
- TestIfElementTouchesCustomElement(x, y); /* for empty space */
-#endif
CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
player->index_bit, dig_side);
else if (element == EL_EXTRA_TIME && level.time > 0)
{
TimeLeft += level.extra_time;
- DrawGameValue_Time(TimeLeft);
+
+ game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
+
+ DisplayGameControlValues();
}
else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
{
if (element == EL_SHIELD_DEADLY)
player->shield_deadly_time_left += level.shield_deadly_time;
}
- else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
+ else if (element == EL_DYNAMITE ||
+ element == EL_EM_DYNAMITE ||
+ element == EL_SP_DISK_RED)
{
if (player->inventory_size < MAX_INVENTORY_SIZE)
player->inventory_element[player->inventory_size++] = element;
- DrawGameValue_Dynamite(local_player->inventory_size);
+ DrawGameDoorValues();
}
else if (element == EL_DYNABOMB_INCREASE_NUMBER)
{
{
player->key[KEY_NR(element)] = TRUE;
- DrawGameValue_Keys(player->key);
+ DrawGameDoorValues();
+ }
+ else if (element == EL_DC_KEY_WHITE)
+ {
+ player->num_white_keys++;
- redraw_mask |= REDRAW_DOOR_1;
+ /* display white keys? */
+ /* DrawGameDoorValues(); */
}
else if (IS_ENVELOPE(element))
{
if (player->inventory_size < MAX_INVENTORY_SIZE)
player->inventory_element[player->inventory_size++] = element;
- DrawGameValue_Dynamite(local_player->inventory_size);
+ DrawGameDoorValues();
}
else if (collect_count > 0)
{
if (local_player->gems_still_needed < 0)
local_player->gems_still_needed = 0;
- DrawGameValue_Emeralds(local_player->gems_still_needed);
+ game.snapshot.collected_item = TRUE;
+
+ game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
+
+ DisplayGameControlValues();
}
RaiseScoreElement(element);
if (mode == DF_SNAP)
{
-#if USE_NEW_SNAP_DELAY
if (level.block_snap_field)
setFieldForSnapping(x, y, element, move_direction);
else
TestIfElementTouchesCustomElement(x, y); /* for empty space */
-#else
- TestIfElementTouchesCustomElement(x, y); /* for empty space */
-#endif
CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
player->index_bit, dig_side);
}
player->is_pushing = TRUE;
+ player->is_active = TRUE;
if (!(IN_LEV_FIELD(nextx, nexty) &&
(IS_FREE(nextx, nexty) ||
- (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
- IS_SB_ELEMENT(element)))))
+ (IS_SB_ELEMENT(element) &&
+ Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
+ (IS_CUSTOM_ELEMENT(element) &&
+ CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
return MP_NO_ACTION;
if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
return MP_NO_ACTION;
}
+ if (IS_CUSTOM_ELEMENT(element) &&
+ CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
+ {
+ if (!DigFieldByCE(nextx, nexty, element))
+ return MP_NO_ACTION;
+ }
+
if (IS_SB_ELEMENT(element))
{
if (element == EL_SOKOBAN_FIELD_FULL)
ACTION_FILLING);
if (local_player->sokobanfields_still_needed == 0 &&
- game.emulation == EMU_SOKOBAN)
+ (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
{
- player->LevelSolved = player->GameOver = TRUE;
+ PlayerWins(player);
+
PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
}
}
ZX = x;
ZY = y;
- DrawLevelField(x, y);
+ game.robot_wheel_active = TRUE;
+
+ TEST_DrawLevelField(x, y);
}
else if (element == EL_SP_TERMINAL)
{
int xx, yy;
-#if 1
SCAN_PLAYFIELD(xx, yy)
-#else
- for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
-#endif
{
if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
+ {
Bang(xx, yy);
+ }
else if (Feld[xx][yy] == EL_SP_TERMINAL)
+ {
Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
+
+ ResetGfxAnimation(xx, yy);
+ TEST_DrawLevelField(xx, yy);
+ }
}
}
else if (IS_BELT_SWITCH(element))
ToggleBeltSwitch(x, y);
}
else if (element == EL_SWITCHGATE_SWITCH_UP ||
- element == EL_SWITCHGATE_SWITCH_DOWN)
+ element == EL_SWITCHGATE_SWITCH_DOWN ||
+ element == EL_DC_SWITCHGATE_SWITCH_UP ||
+ element == EL_DC_SWITCHGATE_SWITCH_DOWN)
{
ToggleSwitchgateSwitch(x, y);
}
{
ToggleLightSwitch(x, y);
}
- else if (element == EL_TIMEGATE_SWITCH)
+ else if (element == EL_TIMEGATE_SWITCH ||
+ element == EL_DC_TIMEGATE_SWITCH)
{
ActivateTimegateSwitch(x, y);
}
local_player->lights_still_needed--;
ResetGfxAnimation(x, y);
- DrawLevelField(x, y);
+ TEST_DrawLevelField(x, y);
}
else if (element == EL_TIME_ORB_FULL)
{
if (level.time > 0 || level.use_time_orb_bug)
{
TimeLeft += level.time_orb_time;
- DrawGameValue_Time(TimeLeft);
+ game.no_time_limit = FALSE;
+
+ game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
+
+ DisplayGameControlValues();
}
ResetGfxAnimation(x, y);
- DrawLevelField(x, y);
+ TEST_DrawLevelField(x, y);
}
else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
game.ball_state = !game.ball_state;
-#if 1
SCAN_PLAYFIELD(xx, yy)
-#else
- for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
-#endif
{
int e = Feld[xx][yy];
if (is_player) /* function can also be called by EL_PENGUIN */
{
if (Feld[x][y] != element) /* really digged/collected something */
+ {
player->is_collecting = !player->is_digging;
+ player->is_active = TRUE;
+ }
}
return MP_MOVING;
}
-boolean SnapField(struct PlayerInfo *player, int dx, int dy)
+static boolean DigFieldByCE(int x, int y, int digging_element)
+{
+ int element = Feld[x][y];
+
+ if (!IS_FREE(x, y))
+ {
+ int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
+ IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
+ ACTION_BREAKING);
+
+ /* no element can dig solid indestructible elements */
+ if (IS_INDESTRUCTIBLE(element) &&
+ !IS_DIGGABLE(element) &&
+ !IS_COLLECTIBLE(element))
+ return FALSE;
+
+ if (AmoebaNr[x][y] &&
+ (element == EL_AMOEBA_FULL ||
+ element == EL_BD_AMOEBA ||
+ element == EL_AMOEBA_GROWING))
+ {
+ AmoebaCnt[AmoebaNr[x][y]]--;
+ AmoebaCnt2[AmoebaNr[x][y]]--;
+ }
+
+ if (IS_MOVING(x, y))
+ RemoveMovingField(x, y);
+ else
+ {
+ RemoveField(x, y);
+ TEST_DrawLevelField(x, y);
+ }
+
+ /* if digged element was about to explode, prevent the explosion */
+ ExplodeField[x][y] = EX_TYPE_NONE;
+
+ PlayLevelSoundAction(x, y, action);
+ }
+
+ Store[x][y] = EL_EMPTY;
+
+ /* this makes it possible to leave the removed element again */
+ if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
+ Store[x][y] = element;
+
+ return TRUE;
+}
+
+static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
{
int jx = player->jx, jy = player->jy;
int x = jx + dx, y = jy + dy;
dx == +1 ? MV_RIGHT :
dy == -1 ? MV_UP :
dy == +1 ? MV_DOWN : MV_NONE);
+ boolean can_continue_snapping = (level.continuous_snapping &&
+ WasJustFalling[x][y] < CHECK_DELAY_FALLING);
if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
return FALSE;
return FALSE;
}
- if (player->is_snapping)
+ /* prevent snapping with already pressed snap key when not allowed */
+ if (player->is_snapping && !can_continue_snapping)
return FALSE;
player->MovDir = snap_direction;
}
player->is_dropping = FALSE;
+ player->is_dropping_pressed = FALSE;
+ player->drop_pressed_delay = 0;
if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
return FALSE;
player->is_snapping = TRUE;
+ player->is_active = TRUE;
if (player->MovPos == 0)
{
}
if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
- DrawLevelField(player->last_jx, player->last_jy);
+ TEST_DrawLevelField(player->last_jx, player->last_jy);
- DrawLevelField(x, y);
+ TEST_DrawLevelField(x, y);
return TRUE;
}
-boolean DropElement(struct PlayerInfo *player)
+static boolean DropElement(struct PlayerInfo *player)
{
int old_element, new_element;
int dropx = player->jx, dropy = player->jy;
int drop_direction = player->MovDir;
int drop_side = drop_direction;
- int drop_element = (player->inventory_size > 0 ?
- player->inventory_element[player->inventory_size - 1] :
- player->inventory_infinite_element != EL_UNDEFINED ?
- player->inventory_infinite_element :
- player->dynabombs_left > 0 ?
- EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
- EL_UNDEFINED);
+ int drop_element = get_next_dropped_element(player);
+
+ player->is_dropping_pressed = TRUE;
/* do not drop an element on top of another element; when holding drop key
pressed without moving, dropped element must move away before the next
if (new_element == EL_UNDEFINED)
return FALSE;
+ /* check if drop key was pressed long enough for EM style dynamite */
+ if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
+ return FALSE;
+
/* check if anything can be dropped at the current position */
if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
return FALSE;
{
player->inventory_size--;
- DrawGameValue_Dynamite(local_player->inventory_size);
+ DrawGameDoorValues();
if (new_element == EL_DYNAMITE)
new_element = EL_DYNAMITE_ACTIVE;
+ else if (new_element == EL_EM_DYNAMITE)
+ new_element = EL_EM_DYNAMITE_ACTIVE;
else if (new_element == EL_SP_DISK_RED)
new_element = EL_SP_DISK_RED_ACTIVE;
}
if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
element_info[new_element].move_pattern == MV_WHEN_DROPPED)
{
- int move_direction, nextx, nexty;
-
if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
MovDir[dropx][dropy] = drop_direction;
- move_direction = MovDir[dropx][dropy];
- nextx = dropx + GET_DX_FROM_DIR(move_direction);
- nexty = dropy + GET_DY_FROM_DIR(move_direction);
-
ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
- CheckCollision[dropx][dropy] = 2;
+
+ /* do not cause impact style collision by dropping elements that can fall */
+ CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
}
player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
player->is_dropping = TRUE;
+ player->drop_pressed_delay = 0;
+ player->is_dropping_pressed = FALSE;
+
player->drop_x = dropx;
player->drop_y = dropy;
PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
}
-void PlayLevelSound_EM(int x, int y, int element_em, int sample)
+void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
{
int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
+ int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
+ int x = xx - 1 - offset;
+ int y = yy - 1 - offset;
switch (sample)
{
break;
case SAMPLE_slurp:
- PlayLevelSoundElementAction(x, y, element, ACTION_SLURPING);
+ PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
break;
case SAMPLE_eater:
PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
break;
- case SAMPLE_wonder:
- PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
- break;
+ case SAMPLE_wonder:
+ PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
+ break;
+
+ case SAMPLE_door:
+ PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
+ break;
+
+ case SAMPLE_exit_open:
+ PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
+ break;
+
+ case SAMPLE_exit_leave:
+ PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
+ break;
+
+ case SAMPLE_dynamite:
+ PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
+ break;
+
+ case SAMPLE_tick:
+ PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
+ break;
+
+ case SAMPLE_press:
+ PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
+ break;
+
+ case SAMPLE_wheel:
+ PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
+ break;
+
+ case SAMPLE_boom:
+ PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
+ break;
+
+ case SAMPLE_die:
+ PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
+ break;
+
+ case SAMPLE_time:
+ PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
+ break;
+
+ default:
+ PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
+ break;
+ }
+}
+
+void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
+{
+ int element = map_element_SP_to_RND(element_sp);
+ int action = map_action_SP_to_RND(action_sp);
+ int offset = (setup.sp_show_border_elements ? 0 : 1);
+ int x = xx - offset;
+ int y = yy - offset;
+
+ PlayLevelSoundElementAction(x, y, element, action);
+}
+
+void RaiseScore(int value)
+{
+ local_player->score += value;
+
+ game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
+
+ DisplayGameControlValues();
+}
+
+void RaiseScoreElement(int element)
+{
+ switch (element)
+ {
+ case EL_EMERALD:
+ case EL_BD_DIAMOND:
+ case EL_EMERALD_YELLOW:
+ case EL_EMERALD_RED:
+ case EL_EMERALD_PURPLE:
+ case EL_SP_INFOTRON:
+ RaiseScore(level.score[SC_EMERALD]);
+ break;
+ case EL_DIAMOND:
+ RaiseScore(level.score[SC_DIAMOND]);
+ break;
+ case EL_CRYSTAL:
+ RaiseScore(level.score[SC_CRYSTAL]);
+ break;
+ case EL_PEARL:
+ RaiseScore(level.score[SC_PEARL]);
+ break;
+ case EL_BUG:
+ case EL_BD_BUTTERFLY:
+ case EL_SP_ELECTRON:
+ RaiseScore(level.score[SC_BUG]);
+ break;
+ case EL_SPACESHIP:
+ case EL_BD_FIREFLY:
+ case EL_SP_SNIKSNAK:
+ RaiseScore(level.score[SC_SPACESHIP]);
+ break;
+ case EL_YAMYAM:
+ case EL_DARK_YAMYAM:
+ RaiseScore(level.score[SC_YAMYAM]);
+ break;
+ case EL_ROBOT:
+ RaiseScore(level.score[SC_ROBOT]);
+ break;
+ case EL_PACMAN:
+ RaiseScore(level.score[SC_PACMAN]);
+ break;
+ case EL_NUT:
+ RaiseScore(level.score[SC_NUT]);
+ break;
+ case EL_DYNAMITE:
+ case EL_EM_DYNAMITE:
+ case EL_SP_DISK_RED:
+ case EL_DYNABOMB_INCREASE_NUMBER:
+ case EL_DYNABOMB_INCREASE_SIZE:
+ case EL_DYNABOMB_INCREASE_POWER:
+ RaiseScore(level.score[SC_DYNAMITE]);
+ break;
+ case EL_SHIELD_NORMAL:
+ case EL_SHIELD_DEADLY:
+ RaiseScore(level.score[SC_SHIELD]);
+ break;
+ case EL_EXTRA_TIME:
+ RaiseScore(level.extra_time_score);
+ break;
+ case EL_KEY_1:
+ case EL_KEY_2:
+ case EL_KEY_3:
+ case EL_KEY_4:
+ case EL_EM_KEY_1:
+ case EL_EM_KEY_2:
+ case EL_EM_KEY_3:
+ case EL_EM_KEY_4:
+ case EL_EMC_KEY_5:
+ case EL_EMC_KEY_6:
+ case EL_EMC_KEY_7:
+ case EL_EMC_KEY_8:
+ case EL_DC_KEY_WHITE:
+ RaiseScore(level.score[SC_KEY]);
+ break;
+ default:
+ RaiseScore(element_info[element].collect_score);
+ break;
+ }
+}
+
+void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
+{
+ if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
+ {
+ /* closing door required in case of envelope style request dialogs */
+ if (!skip_request)
+ CloseDoor(DOOR_CLOSE_1);
+
+#if defined(NETWORK_AVALIABLE)
+ if (options.network)
+ SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
+ else
+#endif
+ {
+ if (quick_quit)
+ FadeSkipNextFadeIn();
+
+ game_status = GAME_MODE_MAIN;
+
+ DrawMainMenu();
+ }
+ }
+ else /* continue playing the game */
+ {
+ if (tape.playing && tape.deactivate_display)
+ TapeDeactivateDisplayOff(TRUE);
+
+ OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
+
+ if (tape.playing && tape.deactivate_display)
+ TapeDeactivateDisplayOn();
+ }
+}
+
+void RequestQuitGame(boolean ask_if_really_quit)
+{
+ boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
+ boolean skip_request = AllPlayersGone || quick_quit;
+
+ RequestQuitGameExt(skip_request, quick_quit,
+ "Do you really want to quit the game?");
+}
+
+
+/* ------------------------------------------------------------------------- */
+/* random generator functions */
+/* ------------------------------------------------------------------------- */
+
+unsigned int InitEngineRandom_RND(int seed)
+{
+ game.num_random_calls = 0;
+
+ return InitEngineRandom(seed);
+}
+
+unsigned int RND(int max)
+{
+ if (max > 0)
+ {
+ game.num_random_calls++;
+
+ return GetEngineRandom(max);
+ }
+
+ return 0;
+}
+
+
+/* ------------------------------------------------------------------------- */
+/* game engine snapshot handling functions */
+/* ------------------------------------------------------------------------- */
+
+struct EngineSnapshotInfo
+{
+ /* runtime values for custom element collect score */
+ int collect_score[NUM_CUSTOM_ELEMENTS];
+
+ /* runtime values for group element choice position */
+ int choice_pos[NUM_GROUP_ELEMENTS];
+
+ /* runtime values for belt position animations */
+ int belt_graphic[4][NUM_BELT_PARTS];
+ int belt_anim_mode[4][NUM_BELT_PARTS];
+};
+
+static struct EngineSnapshotInfo engine_snapshot_rnd;
+static char *snapshot_level_identifier = NULL;
+static int snapshot_level_nr = -1;
+
+static void SaveEngineSnapshotValues_RND()
+{
+ static int belt_base_active_element[4] =
+ {
+ EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
+ EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
+ EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
+ EL_CONVEYOR_BELT_4_LEFT_ACTIVE
+ };
+ int i, j;
+
+ for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
+ {
+ int element = EL_CUSTOM_START + i;
+
+ engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
+ }
+
+ for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
+ {
+ int element = EL_GROUP_START + i;
+
+ engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
+ }
+
+ for (i = 0; i < 4; i++)
+ {
+ for (j = 0; j < NUM_BELT_PARTS; j++)
+ {
+ int element = belt_base_active_element[i] + j;
+ int graphic = el2img(element);
+ int anim_mode = graphic_info[graphic].anim_mode;
+
+ engine_snapshot_rnd.belt_graphic[i][j] = graphic;
+ engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
+ }
+ }
+}
+
+static void LoadEngineSnapshotValues_RND()
+{
+ unsigned int num_random_calls = game.num_random_calls;
+ int i, j;
+
+ for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
+ {
+ int element = EL_CUSTOM_START + i;
+
+ element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
+ }
+
+ for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
+ {
+ int element = EL_GROUP_START + i;
+
+ element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
+ }
+
+ for (i = 0; i < 4; i++)
+ {
+ for (j = 0; j < NUM_BELT_PARTS; j++)
+ {
+ int graphic = engine_snapshot_rnd.belt_graphic[i][j];
+ int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
+
+ graphic_info[graphic].anim_mode = anim_mode;
+ }
+ }
+
+ if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
+ {
+ InitRND(tape.random_seed);
+ for (i = 0; i < num_random_calls; i++)
+ RND(1);
+ }
+
+ if (game.num_random_calls != num_random_calls)
+ {
+ Error(ERR_INFO, "number of random calls out of sync");
+ Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
+ Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
+ Error(ERR_EXIT, "this should not happen -- please debug");
+ }
+}
+
+void FreeEngineSnapshotSingle()
+{
+ FreeSnapshotSingle();
+
+ setString(&snapshot_level_identifier, NULL);
+ snapshot_level_nr = -1;
+}
+
+void FreeEngineSnapshotList()
+{
+ FreeSnapshotList();
+}
+
+ListNode *SaveEngineSnapshotBuffers()
+{
+ ListNode *buffers = NULL;
+
+ /* copy some special values to a structure better suited for the snapshot */
+
+ if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
+ SaveEngineSnapshotValues_RND();
+ if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+ SaveEngineSnapshotValues_EM();
+ if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+ SaveEngineSnapshotValues_SP(&buffers);
+
+ /* save values stored in special snapshot structure */
+
+ if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
+ SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
+ if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+ SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
+ if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+ SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
+
+ /* save further RND engine values */
+
+ SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
+ SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
+ SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
+
+ SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
+ SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
+ SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
+ SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
+
+ SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
+ SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
+ SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
+ SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
+ SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
+
+ SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
+ SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
+ SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
+
+ SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
+
+ SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
+
+ SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
+ SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
+
+ SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
+ SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
+ SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
+ SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
+ SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
+ SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
+ SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
+ SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
+ SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
+ SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
+ SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
+ SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
+ SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
+ SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
+ SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
+ SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
+ SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
+ SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
+
+ SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
+ SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
+
+ SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
+ SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
+ SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
+
+ SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
+ SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
+
+ SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
+ SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
+ SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
+ SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
+ SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
+
+ SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
+ SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
+
+#if 0
+ ListNode *node = engine_snapshot_list_rnd;
+ int num_bytes = 0;
+
+ while (node != NULL)
+ {
+ num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
+
+ node = node->next;
+ }
+
+ printf("::: size of engine snapshot: %d bytes\n", num_bytes);
+#endif
+
+ return buffers;
+}
+
+void SaveEngineSnapshotSingle()
+{
+ ListNode *buffers = SaveEngineSnapshotBuffers();
+
+ /* finally save all snapshot buffers to single snapshot */
+ SaveSnapshotSingle(buffers);
- case SAMPLE_door:
- PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
- break;
+ /* save level identification information */
+ setString(&snapshot_level_identifier, leveldir_current->identifier);
+ snapshot_level_nr = level_nr;
+}
- case SAMPLE_exit_open:
- PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
- break;
+static boolean SaveEngineSnapshotToListExt(boolean initial_snapshot)
+{
+ boolean save_snapshot =
+ (initial_snapshot ||
+ (game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
+ (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
+ game.snapshot.changed_action) ||
+ (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
+ game.snapshot.collected_item));
- case SAMPLE_exit_leave:
- PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
- break;
+ game.snapshot.changed_action = FALSE;
+ game.snapshot.collected_item = FALSE;
- case SAMPLE_dynamite:
- PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
- break;
+ if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
+ tape.quick_resume ||
+ !save_snapshot)
+ return FALSE;
- case SAMPLE_tick:
- PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
- break;
+ ListNode *buffers = SaveEngineSnapshotBuffers();
- case SAMPLE_press:
- PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
- break;
+ /* finally save all snapshot buffers to snapshot list */
+ SaveSnapshotToList(buffers);
- case SAMPLE_wheel:
- PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
- break;
+ return TRUE;
+}
- case SAMPLE_boom:
- PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
- break;
+boolean SaveEngineSnapshotToList()
+{
+ return SaveEngineSnapshotToListExt(FALSE);
+}
- case SAMPLE_die:
- PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
- break;
+void SaveEngineSnapshotToListInitial()
+{
+ FreeEngineSnapshotList();
- case SAMPLE_time:
- PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
- break;
+ SaveEngineSnapshotToListExt(TRUE);
+}
- default:
- PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
- break;
- }
+void LoadEngineSnapshotValues()
+{
+ /* restore special values from snapshot structure */
+
+ if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
+ LoadEngineSnapshotValues_RND();
+ if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+ LoadEngineSnapshotValues_EM();
+ if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+ LoadEngineSnapshotValues_SP();
}
-void RaiseScore(int value)
+void LoadEngineSnapshotSingle()
{
- local_player->score += value;
+ LoadSnapshotSingle();
- DrawGameValue_Score(local_player->score);
+ LoadEngineSnapshotValues();
}
-void RaiseScoreElement(int element)
+void LoadEngineSnapshot_Undo(int steps)
{
- switch(element)
- {
- case EL_EMERALD:
- case EL_BD_DIAMOND:
- case EL_EMERALD_YELLOW:
- case EL_EMERALD_RED:
- case EL_EMERALD_PURPLE:
- case EL_SP_INFOTRON:
- RaiseScore(level.score[SC_EMERALD]);
- break;
- case EL_DIAMOND:
- RaiseScore(level.score[SC_DIAMOND]);
- break;
- case EL_CRYSTAL:
- RaiseScore(level.score[SC_CRYSTAL]);
- break;
- case EL_PEARL:
- RaiseScore(level.score[SC_PEARL]);
- break;
- case EL_BUG:
- case EL_BD_BUTTERFLY:
- case EL_SP_ELECTRON:
- RaiseScore(level.score[SC_BUG]);
- break;
- case EL_SPACESHIP:
- case EL_BD_FIREFLY:
- case EL_SP_SNIKSNAK:
- RaiseScore(level.score[SC_SPACESHIP]);
- break;
- case EL_YAMYAM:
- case EL_DARK_YAMYAM:
- RaiseScore(level.score[SC_YAMYAM]);
- break;
- case EL_ROBOT:
- RaiseScore(level.score[SC_ROBOT]);
- break;
- case EL_PACMAN:
- RaiseScore(level.score[SC_PACMAN]);
- break;
- case EL_NUT:
- RaiseScore(level.score[SC_NUT]);
- break;
- case EL_DYNAMITE:
- case EL_SP_DISK_RED:
- case EL_DYNABOMB_INCREASE_NUMBER:
- case EL_DYNABOMB_INCREASE_SIZE:
- case EL_DYNABOMB_INCREASE_POWER:
- RaiseScore(level.score[SC_DYNAMITE]);
- break;
- case EL_SHIELD_NORMAL:
- case EL_SHIELD_DEADLY:
- RaiseScore(level.score[SC_SHIELD]);
- break;
- case EL_EXTRA_TIME:
- RaiseScore(level.extra_time_score);
- break;
- case EL_KEY_1:
- case EL_KEY_2:
- case EL_KEY_3:
- case EL_KEY_4:
- case EL_EM_KEY_1:
- case EL_EM_KEY_2:
- case EL_EM_KEY_3:
- case EL_EM_KEY_4:
- case EL_EMC_KEY_5:
- case EL_EMC_KEY_6:
- case EL_EMC_KEY_7:
- case EL_EMC_KEY_8:
- RaiseScore(level.score[SC_KEY]);
- break;
- default:
- RaiseScore(element_info[element].collect_score);
- break;
- }
+ LoadSnapshotFromList_Older(steps);
+
+ LoadEngineSnapshotValues();
}
-void RequestQuitGame(boolean ask_if_really_quit)
+void LoadEngineSnapshot_Redo(int steps)
{
- if (AllPlayersGone ||
- !ask_if_really_quit ||
- level_editor_test_game ||
- Request("Do you really want to quit the game ?",
- REQ_ASK | REQ_STAY_CLOSED))
- {
-#if defined(NETWORK_AVALIABLE)
- if (options.network)
- SendToServer_StopPlaying();
- else
-#endif
- {
- game_status = GAME_MODE_MAIN;
- DrawMainMenu();
- }
- }
- else
- {
- if (tape.playing && tape.deactivate_display)
- TapeDeactivateDisplayOff(TRUE);
+ LoadSnapshotFromList_Newer(steps);
- OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
+ LoadEngineSnapshotValues();
+}
- if (tape.playing && tape.deactivate_display)
- TapeDeactivateDisplayOn();
- }
+boolean CheckEngineSnapshotSingle()
+{
+ return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
+ snapshot_level_nr == level_nr);
+}
+
+boolean CheckEngineSnapshotList()
+{
+ return CheckSnapshotList();
}
/* ---------- new game button stuff ---------------------------------------- */
-/* graphic position values for game buttons */
-#define GAME_BUTTON_XSIZE 30
-#define GAME_BUTTON_YSIZE 30
-#define GAME_BUTTON_XPOS 5
-#define GAME_BUTTON_YPOS 215
-#define SOUND_BUTTON_XPOS 5
-#define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
-
-#define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
-#define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
-#define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
-#define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
-#define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
-#define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
-
static struct
{
- int x, y;
+ int graphic;
+ struct XY *pos;
int gadget_id;
char *infotext;
} gamebutton_info[NUM_GAME_BUTTONS] =
{
{
- GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
- GAME_CTRL_ID_STOP,
- "stop game"
+ IMG_GAME_BUTTON_GFX_STOP, &game.button.stop,
+ GAME_CTRL_ID_STOP, "stop game"
+ },
+ {
+ IMG_GAME_BUTTON_GFX_PAUSE, &game.button.pause,
+ GAME_CTRL_ID_PAUSE, "pause game"
+ },
+ {
+ IMG_GAME_BUTTON_GFX_PLAY, &game.button.play,
+ GAME_CTRL_ID_PLAY, "play game"
+ },
+ {
+ IMG_GAME_BUTTON_GFX_UNDO, &game.button.undo,
+ GAME_CTRL_ID_UNDO, "undo step"
+ },
+ {
+ IMG_GAME_BUTTON_GFX_REDO, &game.button.redo,
+ GAME_CTRL_ID_REDO, "redo step"
},
{
- GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
- GAME_CTRL_ID_PAUSE,
- "pause game"
+ IMG_GAME_BUTTON_GFX_SAVE, &game.button.save,
+ GAME_CTRL_ID_SAVE, "save game"
},
{
- GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
- GAME_CTRL_ID_PLAY,
- "play game"
+ IMG_GAME_BUTTON_GFX_PAUSE2, &game.button.pause2,
+ GAME_CTRL_ID_PAUSE2, "pause game"
},
{
- SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
- SOUND_CTRL_ID_MUSIC,
- "background music on/off"
+ IMG_GAME_BUTTON_GFX_LOAD, &game.button.load,
+ GAME_CTRL_ID_LOAD, "load game"
},
{
- SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
- SOUND_CTRL_ID_LOOPS,
- "sound loops on/off"
+ IMG_GAME_BUTTON_GFX_SOUND_MUSIC, &game.button.sound_music,
+ SOUND_CTRL_ID_MUSIC, "background music on/off"
},
{
- SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
- SOUND_CTRL_ID_SIMPLE,
- "normal sounds on/off"
+ IMG_GAME_BUTTON_GFX_SOUND_LOOPS, &game.button.sound_loops,
+ SOUND_CTRL_ID_LOOPS, "sound loops on/off"
+ },
+ {
+ IMG_GAME_BUTTON_GFX_SOUND_SIMPLE, &game.button.sound_simple,
+ SOUND_CTRL_ID_SIMPLE, "normal sounds on/off"
}
};
for (i = 0; i < NUM_GAME_BUTTONS; i++)
{
- Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
+ struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
+ struct XY *pos = gamebutton_info[i].pos;
struct GadgetInfo *gi;
int button_type;
boolean checked;
- unsigned long event_mask;
- int gd_xoffset, gd_yoffset;
- int gd_x1, gd_x2, gd_y1, gd_y2;
+ unsigned int event_mask;
+ int base_x = (tape.show_game_buttons ? VX : DX);
+ int base_y = (tape.show_game_buttons ? VY : DY);
+ int gd_x = gfx->src_x;
+ int gd_y = gfx->src_y;
+ int gd_xp = gfx->src_x + gfx->pressed_xoffset;
+ int gd_yp = gfx->src_y + gfx->pressed_yoffset;
+ int gd_xa = gfx->src_x + gfx->active_xoffset;
+ int gd_ya = gfx->src_y + gfx->active_yoffset;
+ int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
+ int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
int id = i;
- gd_xoffset = gamebutton_info[i].x;
- gd_yoffset = gamebutton_info[i].y;
- gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
- gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
+ if (gfx->bitmap == NULL)
+ {
+ game_gadget[id] = NULL;
+
+ continue;
+ }
if (id == GAME_CTRL_ID_STOP ||
- id == GAME_CTRL_ID_PAUSE ||
- id == GAME_CTRL_ID_PLAY)
+ id == GAME_CTRL_ID_PLAY ||
+ id == GAME_CTRL_ID_SAVE ||
+ id == GAME_CTRL_ID_LOAD)
{
button_type = GD_TYPE_NORMAL_BUTTON;
checked = FALSE;
event_mask = GD_EVENT_RELEASED;
- gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
- gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
+ }
+ else if (id == GAME_CTRL_ID_UNDO ||
+ id == GAME_CTRL_ID_REDO)
+ {
+ button_type = GD_TYPE_NORMAL_BUTTON;
+ checked = FALSE;
+ event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
}
else
{
(id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
(id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
event_mask = GD_EVENT_PRESSED;
- gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
- gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
}
gi = CreateGadget(GDI_CUSTOM_ID, id,
GDI_INFO_TEXT, gamebutton_info[i].infotext,
- GDI_X, DX + gd_xoffset,
- GDI_Y, DY + gd_yoffset,
- GDI_WIDTH, GAME_BUTTON_XSIZE,
- GDI_HEIGHT, GAME_BUTTON_YSIZE,
+ GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
+ GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
+ GDI_WIDTH, gfx->width,
+ GDI_HEIGHT, gfx->height,
GDI_TYPE, button_type,
GDI_STATE, GD_BUTTON_UNPRESSED,
GDI_CHECKED, checked,
- GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
- GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
- GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
- GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
+ GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
+ GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
+ GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
+ GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
+ GDI_DIRECT_DRAW, FALSE,
GDI_EVENT_MASK, event_mask,
GDI_CALLBACK_ACTION, HandleGameButtons,
GDI_END);
FreeGadget(game_gadget[i]);
}
-static void MapGameButtons()
+static void MapGameButtonsAtSamePosition(int id)
+{
+ int i;
+
+ for (i = 0; i < NUM_GAME_BUTTONS; i++)
+ if (i != id &&
+ gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
+ gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
+ MapGadget(game_gadget[i]);
+}
+
+static void UnmapGameButtonsAtSamePosition(int id)
{
int i;
for (i = 0; i < NUM_GAME_BUTTONS; i++)
- MapGadget(game_gadget[i]);
+ if (i != id &&
+ gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
+ gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
+ UnmapGadget(game_gadget[i]);
+}
+
+void MapUndoRedoButtons()
+{
+ UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
+ UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
+
+ MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
+ MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
+}
+
+void UnmapUndoRedoButtons()
+{
+ UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
+ UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
+
+ MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
+ MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
+}
+
+void MapGameButtons()
+{
+ int i;
+
+ for (i = 0; i < NUM_GAME_BUTTONS; i++)
+ if (i != GAME_CTRL_ID_UNDO &&
+ i != GAME_CTRL_ID_REDO)
+ MapGadget(game_gadget[i]);
+
+ if (setup.show_snapshot_buttons)
+ {
+ UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
+ UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
+ UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
+ }
+ else
+ {
+ UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
+ UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
+ UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
+ }
+
+ RedrawGameButtons();
}
void UnmapGameButtons()
UnmapGadget(game_gadget[i]);
}
-static void HandleGameButtons(struct GadgetInfo *gi)
+void RedrawGameButtons()
+{
+ int i;
+
+ for (i = 0; i < NUM_GAME_BUTTONS; i++)
+ RedrawGadget(game_gadget[i]);
+
+ // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
+ redraw_mask &= ~REDRAW_ALL;
+}
+
+void GameUndoRedoExt()
+{
+ ClearPlayerAction();
+
+ tape.pausing = TRUE;
+
+ RedrawPlayfield();
+ UpdateAndDisplayGameControlValues();
+
+ DrawCompleteVideoDisplay();
+ DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
+ DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
+ DrawVideoDisplay((tape.single_step ? VIDEO_STATE_1STEP_ON :
+ VIDEO_STATE_1STEP_OFF), 0);
+
+ BackToFront();
+}
+
+void GameUndo(int steps)
+{
+ if (!CheckEngineSnapshotList())
+ return;
+
+ LoadEngineSnapshot_Undo(steps);
+
+ GameUndoRedoExt();
+}
+
+void GameRedo(int steps)
+{
+ if (!CheckEngineSnapshotList())
+ return;
+
+ LoadEngineSnapshot_Redo(steps);
+
+ GameUndoRedoExt();
+}
+
+static void HandleGameButtonsExt(int id, int button)
{
- int id = gi->custom_id;
+ static boolean game_undo_executed = FALSE;
+ int steps = BUTTON_STEPSIZE(button);
+ boolean handle_game_buttons =
+ (game_status == GAME_MODE_PLAYING ||
+ (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
- if (game_status != GAME_MODE_PLAYING)
+ if (!handle_game_buttons)
return;
switch (id)
{
case GAME_CTRL_ID_STOP:
- RequestQuitGame(TRUE);
+ if (game_status == GAME_MODE_MAIN)
+ break;
+
+ if (tape.playing)
+ TapeStop();
+ else
+ RequestQuitGame(TRUE);
+
break;
case GAME_CTRL_ID_PAUSE:
- if (options.network)
+ case GAME_CTRL_ID_PAUSE2:
+ if (options.network && game_status == GAME_MODE_PLAYING)
{
#if defined(NETWORK_AVALIABLE)
if (tape.pausing)
}
else
TapeTogglePause(TAPE_TOGGLE_MANUAL);
+
+ game_undo_executed = FALSE;
+
break;
case GAME_CTRL_ID_PLAY:
- if (tape.pausing)
+ if (game_status == GAME_MODE_MAIN)
+ {
+ StartGameActions(options.network, setup.autorecord, level.random_seed);
+ }
+ else if (tape.pausing)
{
#if defined(NETWORK_AVALIABLE)
if (options.network)
SendToServer_ContinuePlaying();
else
#endif
- {
- tape.pausing = FALSE;
- DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
- }
+ TapeTogglePause(TAPE_TOGGLE_MANUAL);
}
break;
+ case GAME_CTRL_ID_UNDO:
+ // Important: When using "save snapshot when collecting an item" mode,
+ // load last (current) snapshot for first "undo" after pressing "pause"
+ // (else the last-but-one snapshot would be loaded, because the snapshot
+ // pointer already points to the last snapshot when pressing "pause",
+ // which is fine for "every step/move" mode, but not for "every collect")
+ if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
+ !game_undo_executed)
+ steps--;
+
+ game_undo_executed = TRUE;
+
+ GameUndo(steps);
+ break;
+
+ case GAME_CTRL_ID_REDO:
+ GameRedo(steps);
+ break;
+
+ case GAME_CTRL_ID_SAVE:
+ TapeQuickSave();
+ break;
+
+ case GAME_CTRL_ID_LOAD:
+ TapeQuickLoad();
+ break;
+
case SOUND_CTRL_ID_MUSIC:
if (setup.sound_music)
{
setup.sound_music = FALSE;
+
FadeMusic();
}
else if (audio.music_available)
else if (audio.loops_available)
{
setup.sound = setup.sound_loops = TRUE;
+
SetAudioMode(setup.sound);
}
break;
else if (audio.sound_available)
{
setup.sound = setup.sound_simple = TRUE;
+
SetAudioMode(setup.sound);
}
break;
break;
}
}
+
+static void HandleGameButtons(struct GadgetInfo *gi)
+{
+ HandleGameButtonsExt(gi->custom_id, gi->event.button);
+}
+
+void HandleSoundButtonKeys(Key key)
+{
+
+ if (key == setup.shortcut.sound_simple)
+ ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
+ else if (key == setup.shortcut.sound_loops)
+ ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
+ else if (key == setup.shortcut.sound_music)
+ ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
+}