1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include "libgame/libgame.h"
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF ( 1)
30 #define USE_NEW_SP_SLIPPERY (USE_NEW_STUFF * 1)
31 #define USE_NEW_CUSTOM_VALUE (USE_NEW_STUFF * 1)
32 #define USE_NEW_PLAYER_ANIM (USE_NEW_STUFF * 1)
33 #define USE_NEW_ALL_SLIPPERY (USE_NEW_STUFF * 1)
34 #define USE_NEW_PLAYER_SPEED (USE_NEW_STUFF * 1)
35 #define USE_NEW_DELAYED_ACTION (USE_NEW_STUFF * 1)
36 #define USE_NEW_SNAP_DELAY (USE_NEW_STUFF * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME (USE_NEW_STUFF * 0)
44 /* for MovePlayer() */
45 #define MF_NO_ACTION 0
49 /* for ScrollPlayer() */
51 #define SCROLL_GO_ON 1
54 #define EX_PHASE_START 0
55 #define EX_TYPE_NONE 0
56 #define EX_TYPE_NORMAL (1 << 0)
57 #define EX_TYPE_CENTER (1 << 1)
58 #define EX_TYPE_BORDER (1 << 2)
59 #define EX_TYPE_CROSS (1 << 3)
60 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
62 /* special positions in the game control window (relative to control window) */
65 #define XX_EMERALDS 29
66 #define YY_EMERALDS 54
67 #define XX_DYNAMITE 29
68 #define YY_DYNAMITE 89
77 /* special positions in the game control window (relative to main window) */
78 #define DX_LEVEL (DX + XX_LEVEL)
79 #define DY_LEVEL (DY + YY_LEVEL)
80 #define DX_EMERALDS (DX + XX_EMERALDS)
81 #define DY_EMERALDS (DY + YY_EMERALDS)
82 #define DX_DYNAMITE (DX + XX_DYNAMITE)
83 #define DY_DYNAMITE (DY + YY_DYNAMITE)
84 #define DX_KEYS (DX + XX_KEYS)
85 #define DY_KEYS (DY + YY_KEYS)
86 #define DX_SCORE (DX + XX_SCORE)
87 #define DY_SCORE (DY + YY_SCORE)
88 #define DX_TIME1 (DX + XX_TIME1)
89 #define DX_TIME2 (DX + XX_TIME2)
90 #define DY_TIME (DY + YY_TIME)
92 /* values for initial player move delay (initial delay counter value) */
93 #define INITIAL_MOVE_DELAY_OFF -1
94 #define INITIAL_MOVE_DELAY_ON 0
96 /* values for player movement speed (which is in fact a delay value) */
97 #define MOVE_DELAY_MIN_SPEED 32
98 #define MOVE_DELAY_NORMAL_SPEED 8
99 #define MOVE_DELAY_HIGH_SPEED 4
100 #define MOVE_DELAY_MAX_SPEED 1
103 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
104 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
106 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
107 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
109 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
110 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
112 /* values for other actions */
113 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
114 #define MOVE_STEPSIZE_MIN (1)
115 #define MOVE_STEPSIZE_MAX (TILEX)
117 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
118 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
120 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
122 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
123 RND(element_info[e].push_delay_random))
124 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
125 RND(element_info[e].drop_delay_random))
126 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
127 RND(element_info[e].move_delay_random))
128 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
129 (element_info[e].move_delay_random))
130 #define GET_NEW_CUSTOM_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
131 RND(element_info[e].ce_value_random_initial))
132 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
133 RND((c)->delay_random * (c)->delay_frames))
135 #define GET_TARGET_ELEMENT(e, ch) \
136 ((e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
137 (e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : (e))
139 #define CAN_GROW_INTO(e) \
140 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
142 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
143 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
146 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
147 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
148 (CAN_MOVE_INTO_ACID(e) && \
149 Feld[x][y] == EL_ACID) || \
152 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
153 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
154 (CAN_MOVE_INTO_ACID(e) && \
155 Feld[x][y] == EL_ACID) || \
158 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
159 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
161 (CAN_MOVE_INTO_ACID(e) && \
162 Feld[x][y] == EL_ACID) || \
163 (DONT_COLLIDE_WITH(e) && \
165 !PLAYER_ENEMY_PROTECTED(x, y))))
167 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
168 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
170 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
171 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
173 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
174 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
176 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
177 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
179 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
180 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
182 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
183 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
185 #define PIG_CAN_ENTER_FIELD(e, x, y) \
186 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
188 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
189 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
190 IS_FOOD_PENGUIN(Feld[x][y])))
191 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
192 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
194 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
195 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
197 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
198 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
200 #define GROUP_NR(e) ((e) - EL_GROUP_START)
201 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
202 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
203 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
205 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
206 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
208 #define CE_ENTER_FIELD_COND(e, x, y) \
209 (!IS_PLAYER(x, y) && \
210 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
212 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
213 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
215 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
216 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
218 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
219 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
220 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
221 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
223 /* game button identifiers */
224 #define GAME_CTRL_ID_STOP 0
225 #define GAME_CTRL_ID_PAUSE 1
226 #define GAME_CTRL_ID_PLAY 2
227 #define SOUND_CTRL_ID_MUSIC 3
228 #define SOUND_CTRL_ID_LOOPS 4
229 #define SOUND_CTRL_ID_SIMPLE 5
231 #define NUM_GAME_BUTTONS 6
234 /* forward declaration for internal use */
236 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
237 static void AdvanceFrameAndPlayerCounters(int);
239 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
240 static boolean MovePlayer(struct PlayerInfo *, int, int);
241 static void ScrollPlayer(struct PlayerInfo *, int);
242 static void ScrollScreen(struct PlayerInfo *, int);
244 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
246 static void InitBeltMovement(void);
247 static void CloseAllOpenTimegates(void);
248 static void CheckGravityMovement(struct PlayerInfo *);
249 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
250 static void KillPlayerUnlessEnemyProtected(int, int);
251 static void KillPlayerUnlessExplosionProtected(int, int);
253 static void TestIfPlayerTouchesCustomElement(int, int);
254 static void TestIfElementTouchesCustomElement(int, int);
255 static void TestIfElementHitsCustomElement(int, int, int);
257 static void TestIfElementSmashesCustomElement(int, int, int);
260 static void ChangeElement(int, int, int);
262 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
263 #define CheckTriggeredElementChange(x, y, e, ev) \
264 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
265 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
266 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
267 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
268 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
269 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
270 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
272 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
273 #define CheckElementChange(x, y, e, te, ev) \
274 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
275 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
276 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
277 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
278 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
280 static void PlayLevelSound(int, int, int);
281 static void PlayLevelSoundNearest(int, int, int);
282 static void PlayLevelSoundAction(int, int, int);
283 static void PlayLevelSoundElementAction(int, int, int, int);
284 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
285 static void PlayLevelSoundActionIfLoop(int, int, int);
286 static void StopLevelSoundActionIfLoop(int, int, int);
287 static void PlayLevelMusic();
289 static void MapGameButtons();
290 static void HandleGameButtons(struct GadgetInfo *);
292 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
295 /* ------------------------------------------------------------------------- */
296 /* definition of elements that automatically change to other elements after */
297 /* a specified time, eventually calling a function when changing */
298 /* ------------------------------------------------------------------------- */
300 /* forward declaration for changer functions */
301 static void InitBuggyBase(int x, int y);
302 static void WarnBuggyBase(int x, int y);
304 static void InitTrap(int x, int y);
305 static void ActivateTrap(int x, int y);
306 static void ChangeActiveTrap(int x, int y);
308 static void InitRobotWheel(int x, int y);
309 static void RunRobotWheel(int x, int y);
310 static void StopRobotWheel(int x, int y);
312 static void InitTimegateWheel(int x, int y);
313 static void RunTimegateWheel(int x, int y);
315 struct ChangingElementInfo
320 void (*pre_change_function)(int x, int y);
321 void (*change_function)(int x, int y);
322 void (*post_change_function)(int x, int y);
325 static struct ChangingElementInfo change_delay_list[] =
376 EL_SWITCHGATE_OPENING,
384 EL_SWITCHGATE_CLOSING,
385 EL_SWITCHGATE_CLOSED,
417 EL_ACID_SPLASH_RIGHT,
426 EL_SP_BUGGY_BASE_ACTIVATING,
433 EL_SP_BUGGY_BASE_ACTIVATING,
434 EL_SP_BUGGY_BASE_ACTIVE,
441 EL_SP_BUGGY_BASE_ACTIVE,
465 EL_ROBOT_WHEEL_ACTIVE,
473 EL_TIMEGATE_SWITCH_ACTIVE,
494 int push_delay_fixed, push_delay_random;
499 { EL_BALLOON, 0, 0 },
501 { EL_SOKOBAN_OBJECT, 2, 0 },
502 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
503 { EL_SATELLITE, 2, 0 },
504 { EL_SP_DISK_YELLOW, 2, 0 },
506 { EL_UNDEFINED, 0, 0 },
514 move_stepsize_list[] =
516 { EL_AMOEBA_DROP, 2 },
517 { EL_AMOEBA_DROPPING, 2 },
518 { EL_QUICKSAND_FILLING, 1 },
519 { EL_QUICKSAND_EMPTYING, 1 },
520 { EL_MAGIC_WALL_FILLING, 2 },
521 { EL_BD_MAGIC_WALL_FILLING, 2 },
522 { EL_MAGIC_WALL_EMPTYING, 2 },
523 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
533 collect_count_list[] =
536 { EL_BD_DIAMOND, 1 },
537 { EL_EMERALD_YELLOW, 1 },
538 { EL_EMERALD_RED, 1 },
539 { EL_EMERALD_PURPLE, 1 },
541 { EL_SP_INFOTRON, 1 },
553 access_direction_list[] =
555 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
556 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
557 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
558 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
559 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
560 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
561 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
562 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
563 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
564 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
565 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
567 { EL_SP_PORT_LEFT, MV_RIGHT },
568 { EL_SP_PORT_RIGHT, MV_LEFT },
569 { EL_SP_PORT_UP, MV_DOWN },
570 { EL_SP_PORT_DOWN, MV_UP },
571 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
572 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
573 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
574 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
575 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
576 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
577 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
578 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
579 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
580 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
581 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
582 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
583 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
584 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
585 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
587 { EL_UNDEFINED, MV_NONE }
590 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
592 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
593 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
594 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
595 IS_JUST_CHANGING(x, y))
597 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
600 void GetPlayerConfig()
602 if (!audio.sound_available)
603 setup.sound_simple = FALSE;
605 if (!audio.loops_available)
606 setup.sound_loops = FALSE;
608 if (!audio.music_available)
609 setup.sound_music = FALSE;
611 if (!video.fullscreen_available)
612 setup.fullscreen = FALSE;
614 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
616 SetAudioMode(setup.sound);
620 static int getBeltNrFromBeltElement(int element)
622 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
623 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
624 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
627 static int getBeltNrFromBeltActiveElement(int element)
629 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
630 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
631 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
634 static int getBeltNrFromBeltSwitchElement(int element)
636 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
637 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
638 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
641 static int getBeltDirNrFromBeltSwitchElement(int element)
643 static int belt_base_element[4] =
645 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
646 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
647 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
648 EL_CONVEYOR_BELT_4_SWITCH_LEFT
651 int belt_nr = getBeltNrFromBeltSwitchElement(element);
652 int belt_dir_nr = element - belt_base_element[belt_nr];
654 return (belt_dir_nr % 3);
657 static int getBeltDirFromBeltSwitchElement(int element)
659 static int belt_move_dir[3] =
666 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
668 return belt_move_dir[belt_dir_nr];
671 static void InitPlayerField(int x, int y, int element, boolean init_game)
673 if (element == EL_SP_MURPHY)
677 if (stored_player[0].present)
679 Feld[x][y] = EL_SP_MURPHY_CLONE;
685 stored_player[0].use_murphy = TRUE;
688 Feld[x][y] = EL_PLAYER_1;
694 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
695 int jx = player->jx, jy = player->jy;
697 player->present = TRUE;
699 player->block_last_field = (element == EL_SP_MURPHY ?
700 level.sp_block_last_field :
701 level.block_last_field);
703 /* ---------- initialize player's last field block delay --------------- */
705 /* always start with reliable default value (no adjustment needed) */
706 player->block_delay_adjustment = 0;
708 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
709 if (player->block_last_field && element == EL_SP_MURPHY)
710 player->block_delay_adjustment = 1;
712 /* special case 2: in game engines before 3.1.1, blocking was different */
713 if (game.use_block_last_field_bug)
714 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
716 if (!options.network || player->connected)
718 player->active = TRUE;
720 /* remove potentially duplicate players */
721 if (StorePlayer[jx][jy] == Feld[x][y])
722 StorePlayer[jx][jy] = 0;
724 StorePlayer[x][y] = Feld[x][y];
728 printf("Player %d activated.\n", player->element_nr);
729 printf("[Local player is %d and currently %s.]\n",
730 local_player->element_nr,
731 local_player->active ? "active" : "not active");
735 Feld[x][y] = EL_EMPTY;
737 player->jx = player->last_jx = x;
738 player->jy = player->last_jy = y;
742 static void InitField(int x, int y, boolean init_game)
744 int element = Feld[x][y];
753 InitPlayerField(x, y, element, init_game);
756 case EL_SOKOBAN_FIELD_PLAYER:
757 element = Feld[x][y] = EL_PLAYER_1;
758 InitField(x, y, init_game);
760 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
761 InitField(x, y, init_game);
764 case EL_SOKOBAN_FIELD_EMPTY:
765 local_player->sokobanfields_still_needed++;
769 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
770 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
771 else if (x > 0 && Feld[x-1][y] == EL_ACID)
772 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
773 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
774 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
775 else if (y > 0 && Feld[x][y-1] == EL_ACID)
776 Feld[x][y] = EL_ACID_POOL_BOTTOM;
777 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
778 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
786 case EL_SPACESHIP_RIGHT:
787 case EL_SPACESHIP_UP:
788 case EL_SPACESHIP_LEFT:
789 case EL_SPACESHIP_DOWN:
791 case EL_BD_BUTTERFLY_RIGHT:
792 case EL_BD_BUTTERFLY_UP:
793 case EL_BD_BUTTERFLY_LEFT:
794 case EL_BD_BUTTERFLY_DOWN:
795 case EL_BD_BUTTERFLY:
796 case EL_BD_FIREFLY_RIGHT:
797 case EL_BD_FIREFLY_UP:
798 case EL_BD_FIREFLY_LEFT:
799 case EL_BD_FIREFLY_DOWN:
801 case EL_PACMAN_RIGHT:
825 if (y == lev_fieldy - 1)
827 Feld[x][y] = EL_AMOEBA_GROWING;
828 Store[x][y] = EL_AMOEBA_WET;
832 case EL_DYNAMITE_ACTIVE:
833 case EL_SP_DISK_RED_ACTIVE:
834 case EL_DYNABOMB_PLAYER_1_ACTIVE:
835 case EL_DYNABOMB_PLAYER_2_ACTIVE:
836 case EL_DYNABOMB_PLAYER_3_ACTIVE:
837 case EL_DYNABOMB_PLAYER_4_ACTIVE:
842 local_player->lights_still_needed++;
846 local_player->friends_still_needed++;
851 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
854 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
855 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
856 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
857 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
858 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
859 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
860 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
861 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
862 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
863 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
864 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
865 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
868 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
869 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
870 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
872 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
874 game.belt_dir[belt_nr] = belt_dir;
875 game.belt_dir_nr[belt_nr] = belt_dir_nr;
877 else /* more than one switch -- set it like the first switch */
879 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
884 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
886 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
889 case EL_LIGHT_SWITCH_ACTIVE:
891 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
896 if (IS_CUSTOM_ELEMENT(element))
898 if (CAN_MOVE(element))
901 #if USE_NEW_CUSTOM_VALUE
902 if (!element_info[element].use_last_ce_value || init_game)
903 CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]);
907 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
910 else if (IS_GROUP_ELEMENT(element))
912 struct ElementGroupInfo *group = element_info[element].group;
913 int last_anim_random_frame = gfx.anim_random_frame;
916 if (group->choice_mode == ANIM_RANDOM)
917 gfx.anim_random_frame = RND(group->num_elements_resolved);
919 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
920 group->choice_mode, 0,
923 if (group->choice_mode == ANIM_RANDOM)
924 gfx.anim_random_frame = last_anim_random_frame;
928 Feld[x][y] = group->element_resolved[element_pos];
930 InitField(x, y, init_game);
937 #if USE_NEW_CUSTOM_VALUE
940 CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]);
942 CustomValue[x][y] = element_info[Feld[x][y]].custom_value_initial;
950 static inline void InitField_WithBug1(int x, int y, boolean init_game)
952 InitField(x, y, init_game);
954 /* not needed to call InitMovDir() -- already done by InitField()! */
955 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
956 CAN_MOVE(Feld[x][y]))
960 static inline void InitField_WithBug2(int x, int y, boolean init_game)
962 int old_element = Feld[x][y];
964 InitField(x, y, init_game);
966 /* not needed to call InitMovDir() -- already done by InitField()! */
967 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
968 CAN_MOVE(old_element) &&
969 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
972 /* this case is in fact a combination of not less than three bugs:
973 first, it calls InitMovDir() for elements that can move, although this is
974 already done by InitField(); then, it checks the element that was at this
975 field _before_ the call to InitField() (which can change it); lastly, it
976 was not called for "mole with direction" elements, which were treated as
977 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
981 inline void DrawGameValue_Emeralds(int value)
983 DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
986 inline void DrawGameValue_Dynamite(int value)
988 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
991 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
995 /* currently only 4 of 8 possible keys are displayed */
996 for (i = 0; i < STD_NUM_KEYS; i++)
999 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1000 el2edimg(EL_KEY_1 + i));
1002 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1003 DOOR_GFX_PAGEX5 + XX_KEYS + i * MINI_TILEX, YY_KEYS,
1004 MINI_TILEX, MINI_TILEY, DX_KEYS + i * MINI_TILEX, DY_KEYS);
1008 inline void DrawGameValue_Score(int value)
1010 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1013 inline void DrawGameValue_Time(int value)
1016 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1018 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1021 inline void DrawGameValue_Level(int value)
1024 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1027 /* misuse area for displaying emeralds to draw bigger level number */
1028 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1029 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1031 /* now copy it to the area for displaying level number */
1032 BlitBitmap(drawto, drawto,
1033 DX_EMERALDS, DY_EMERALDS + 1,
1034 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1035 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1036 DX_LEVEL - 1, DY_LEVEL + 1);
1038 /* restore the area for displaying emeralds */
1039 DrawGameValue_Emeralds(local_player->gems_still_needed);
1041 /* yes, this is all really ugly :-) */
1045 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1048 int key[MAX_NUM_KEYS];
1051 for (i = 0; i < MAX_NUM_KEYS; i++)
1052 key[i] = key_bits & (1 << i);
1054 DrawGameValue_Level(level_nr);
1056 DrawGameValue_Emeralds(emeralds);
1057 DrawGameValue_Dynamite(dynamite);
1058 DrawGameValue_Score(score);
1059 DrawGameValue_Time(time);
1061 DrawGameValue_Keys(key);
1064 void DrawGameDoorValues()
1068 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1070 DrawGameDoorValues_EM();
1075 DrawGameValue_Level(level_nr);
1077 DrawGameValue_Emeralds(local_player->gems_still_needed);
1078 DrawGameValue_Dynamite(local_player->inventory_size);
1079 DrawGameValue_Score(local_player->score);
1080 DrawGameValue_Time(TimeLeft);
1082 for (i = 0; i < MAX_PLAYERS; i++)
1083 DrawGameValue_Keys(stored_player[i].key);
1086 static void resolve_group_element(int group_element, int recursion_depth)
1088 static int group_nr;
1089 static struct ElementGroupInfo *group;
1090 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1093 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1095 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1096 group_element - EL_GROUP_START + 1);
1098 /* replace element which caused too deep recursion by question mark */
1099 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1104 if (recursion_depth == 0) /* initialization */
1106 group = element_info[group_element].group;
1107 group_nr = group_element - EL_GROUP_START;
1109 group->num_elements_resolved = 0;
1110 group->choice_pos = 0;
1113 for (i = 0; i < actual_group->num_elements; i++)
1115 int element = actual_group->element[i];
1117 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1120 if (IS_GROUP_ELEMENT(element))
1121 resolve_group_element(element, recursion_depth + 1);
1124 group->element_resolved[group->num_elements_resolved++] = element;
1125 element_info[element].in_group[group_nr] = TRUE;
1132 =============================================================================
1134 -----------------------------------------------------------------------------
1135 initialize game engine due to level / tape version number
1136 =============================================================================
1139 static void InitGameEngine()
1143 /* set game engine from tape file when re-playing, else from level file */
1144 game.engine_version = (tape.playing ? tape.engine_version :
1145 level.game_version);
1147 /* ---------------------------------------------------------------------- */
1148 /* set flags for bugs and changes according to active game engine version */
1149 /* ---------------------------------------------------------------------- */
1152 Summary of bugfix/change:
1153 Fixed handling for custom elements that change when pushed by the player.
1155 Fixed/changed in version:
1159 Before 3.1.0, custom elements that "change when pushing" changed directly
1160 after the player started pushing them (until then handled in "DigField()").
1161 Since 3.1.0, these custom elements are not changed until the "pushing"
1162 move of the element is finished (now handled in "ContinueMoving()").
1164 Affected levels/tapes:
1165 The first condition is generally needed for all levels/tapes before version
1166 3.1.0, which might use the old behaviour before it was changed; known tapes
1167 that are affected are some tapes from the level set "Walpurgis Gardens" by
1169 The second condition is an exception from the above case and is needed for
1170 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1171 above (including some development versions of 3.1.0), but before it was
1172 known that this change would break tapes like the above and was fixed in
1173 3.1.1, so that the changed behaviour was active although the engine version
1174 while recording maybe was before 3.1.0. There is at least one tape that is
1175 affected by this exception, which is the tape for the one-level set "Bug
1176 Machine" by Juergen Bonhagen.
1179 game.use_change_when_pushing_bug =
1180 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1182 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1183 tape.game_version < VERSION_IDENT(3,1,1,0)));
1186 Summary of bugfix/change:
1187 Fixed handling for blocking the field the player leaves when moving.
1189 Fixed/changed in version:
1193 Before 3.1.1, when "block last field when moving" was enabled, the field
1194 the player is leaving when moving was blocked for the time of the move,
1195 and was directly unblocked afterwards. This resulted in the last field
1196 being blocked for exactly one less than the number of frames of one player
1197 move. Additionally, even when blocking was disabled, the last field was
1198 blocked for exactly one frame.
1199 Since 3.1.1, due to changes in player movement handling, the last field
1200 is not blocked at all when blocking is disabled. When blocking is enabled,
1201 the last field is blocked for exactly the number of frames of one player
1202 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1203 last field is blocked for exactly one more than the number of frames of
1206 Affected levels/tapes:
1207 (!!! yet to be determined -- probably many !!!)
1210 game.use_block_last_field_bug =
1211 (game.engine_version < VERSION_IDENT(3,1,1,0));
1214 Summary of bugfix/change:
1215 Changed behaviour of CE changes with multiple changes per single frame.
1217 Fixed/changed in version:
1221 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
1222 This resulted in race conditions where CEs seem to behave strange in some
1223 situations (where triggered CE changes were just skipped because there was
1224 already a CE change on that tile in the playfield in that engine frame).
1225 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
1226 (The number of changes per frame must be limited in any case, because else
1227 it is easily possible to define CE changes that would result in an infinite
1228 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
1229 should be set large enough so that it would only be reached in cases where
1230 the corresponding CE change conditions run into a loop. Therefore, it seems
1231 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
1232 maximal number of change pages for custom elements.)
1234 Affected levels/tapes:
1238 #if USE_ONLY_ONE_CHANGE_PER_FRAME
1239 game.max_num_changes_per_frame = 1;
1241 game.max_num_changes_per_frame =
1242 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
1245 /* ---------------------------------------------------------------------- */
1247 /* dynamically adjust element properties according to game engine version */
1248 InitElementPropertiesEngine(game.engine_version);
1251 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1252 printf(" tape version == %06d [%s] [file: %06d]\n",
1253 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1255 printf(" => game.engine_version == %06d\n", game.engine_version);
1258 /* ---------- recursively resolve group elements ------------------------- */
1260 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1261 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1262 element_info[i].in_group[j] = FALSE;
1264 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1265 resolve_group_element(EL_GROUP_START + i, 0);
1267 /* ---------- initialize player's initial move delay --------------------- */
1269 /* dynamically adjust player properties according to level information */
1270 game.initial_move_delay_value =
1271 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1273 /* dynamically adjust player properties according to game engine version */
1274 game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1275 game.initial_move_delay_value : 0);
1277 /* ---------- initialize player's initial push delay --------------------- */
1279 /* dynamically adjust player properties according to game engine version */
1280 game.initial_push_delay_value =
1281 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1283 /* ---------- initialize changing elements ------------------------------- */
1285 /* initialize changing elements information */
1286 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1288 struct ElementInfo *ei = &element_info[i];
1290 /* this pointer might have been changed in the level editor */
1291 ei->change = &ei->change_page[0];
1293 if (!IS_CUSTOM_ELEMENT(i))
1295 ei->change->target_element = EL_EMPTY_SPACE;
1296 ei->change->delay_fixed = 0;
1297 ei->change->delay_random = 0;
1298 ei->change->delay_frames = 1;
1301 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1303 ei->has_change_event[j] = FALSE;
1305 ei->event_page_nr[j] = 0;
1306 ei->event_page[j] = &ei->change_page[0];
1310 /* add changing elements from pre-defined list */
1311 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1313 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1314 struct ElementInfo *ei = &element_info[ch_delay->element];
1316 ei->change->target_element = ch_delay->target_element;
1317 ei->change->delay_fixed = ch_delay->change_delay;
1319 ei->change->pre_change_function = ch_delay->pre_change_function;
1320 ei->change->change_function = ch_delay->change_function;
1321 ei->change->post_change_function = ch_delay->post_change_function;
1323 ei->change->can_change = TRUE;
1324 ei->change->can_change_or_has_action = TRUE;
1326 ei->has_change_event[CE_DELAY] = TRUE;
1328 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1329 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1332 /* ---------- initialize internal run-time variables ------------- */
1334 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1336 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1338 for (j = 0; j < ei->num_change_pages; j++)
1340 ei->change_page[j].can_change_or_has_action =
1341 (ei->change_page[j].can_change |
1342 ei->change_page[j].has_action);
1346 /* add change events from custom element configuration */
1347 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1349 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1351 for (j = 0; j < ei->num_change_pages; j++)
1353 if (!ei->change_page[j].can_change_or_has_action)
1356 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1358 /* only add event page for the first page found with this event */
1359 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1361 ei->has_change_event[k] = TRUE;
1363 ei->event_page_nr[k] = j;
1364 ei->event_page[k] = &ei->change_page[j];
1370 /* ---------- initialize run-time trigger player and element ------------- */
1372 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1374 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1376 for (j = 0; j < ei->num_change_pages; j++)
1378 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1379 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1380 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
1381 ei->change_page[j].actual_trigger_ce_value = 0;
1385 /* ---------- initialize trigger events ---------------------------------- */
1387 /* initialize trigger events information */
1388 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1389 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1390 trigger_events[i][j] = FALSE;
1392 /* add trigger events from element change event properties */
1393 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1395 struct ElementInfo *ei = &element_info[i];
1397 for (j = 0; j < ei->num_change_pages; j++)
1399 if (!ei->change_page[j].can_change_or_has_action)
1402 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1404 int trigger_element = ei->change_page[j].trigger_element;
1406 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1408 if (ei->change_page[j].has_event[k])
1410 if (IS_GROUP_ELEMENT(trigger_element))
1412 struct ElementGroupInfo *group =
1413 element_info[trigger_element].group;
1415 for (l = 0; l < group->num_elements_resolved; l++)
1416 trigger_events[group->element_resolved[l]][k] = TRUE;
1419 trigger_events[trigger_element][k] = TRUE;
1426 /* ---------- initialize push delay -------------------------------------- */
1428 /* initialize push delay values to default */
1429 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1431 if (!IS_CUSTOM_ELEMENT(i))
1433 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1434 element_info[i].push_delay_random = game.default_push_delay_random;
1438 /* set push delay value for certain elements from pre-defined list */
1439 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1441 int e = push_delay_list[i].element;
1443 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1444 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1447 /* set push delay value for Supaplex elements for newer engine versions */
1448 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1450 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1452 if (IS_SP_ELEMENT(i))
1454 /* set SP push delay to just enough to push under a falling zonk */
1455 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1457 element_info[i].push_delay_fixed = delay;
1458 element_info[i].push_delay_random = 0;
1463 /* ---------- initialize move stepsize ----------------------------------- */
1465 /* initialize move stepsize values to default */
1466 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1467 if (!IS_CUSTOM_ELEMENT(i))
1468 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1470 /* set move stepsize value for certain elements from pre-defined list */
1471 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1473 int e = move_stepsize_list[i].element;
1475 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1478 /* ---------- initialize collect score ----------------------------------- */
1480 /* initialize collect score values for custom elements from initial value */
1481 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1482 if (IS_CUSTOM_ELEMENT(i))
1483 element_info[i].collect_score = element_info[i].collect_score_initial;
1485 /* ---------- initialize collect count ----------------------------------- */
1487 /* initialize collect count values for non-custom elements */
1488 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1489 if (!IS_CUSTOM_ELEMENT(i))
1490 element_info[i].collect_count_initial = 0;
1492 /* add collect count values for all elements from pre-defined list */
1493 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1494 element_info[collect_count_list[i].element].collect_count_initial =
1495 collect_count_list[i].count;
1497 /* ---------- initialize access direction -------------------------------- */
1499 /* initialize access direction values to default (access from every side) */
1500 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1501 if (!IS_CUSTOM_ELEMENT(i))
1502 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1504 /* set access direction value for certain elements from pre-defined list */
1505 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1506 element_info[access_direction_list[i].element].access_direction =
1507 access_direction_list[i].direction;
1510 int get_num_special_action(int element, int action_first, int action_last)
1512 int num_special_action = 0;
1515 for (i = action_first; i <= action_last; i++)
1517 boolean found = FALSE;
1519 for (j = 0; j < NUM_DIRECTIONS; j++)
1520 if (el_act_dir2img(element, i, j) !=
1521 el_act_dir2img(element, ACTION_DEFAULT, j))
1525 num_special_action++;
1530 return num_special_action;
1534 =============================================================================
1536 -----------------------------------------------------------------------------
1537 initialize and start new game
1538 =============================================================================
1543 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1544 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1545 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1550 /* don't play tapes over network */
1551 network_playing = (options.network && !tape.playing);
1553 for (i = 0; i < MAX_PLAYERS; i++)
1555 struct PlayerInfo *player = &stored_player[i];
1557 player->index_nr = i;
1558 player->index_bit = (1 << i);
1559 player->element_nr = EL_PLAYER_1 + i;
1561 player->present = FALSE;
1562 player->active = FALSE;
1565 player->effective_action = 0;
1566 player->programmed_action = 0;
1569 player->gems_still_needed = level.gems_needed;
1570 player->sokobanfields_still_needed = 0;
1571 player->lights_still_needed = 0;
1572 player->friends_still_needed = 0;
1574 for (j = 0; j < MAX_NUM_KEYS; j++)
1575 player->key[j] = FALSE;
1577 player->dynabomb_count = 0;
1578 player->dynabomb_size = 1;
1579 player->dynabombs_left = 0;
1580 player->dynabomb_xl = FALSE;
1582 player->MovDir = MV_NONE;
1585 player->GfxDir = MV_NONE;
1586 player->GfxAction = ACTION_DEFAULT;
1588 player->StepFrame = 0;
1590 player->use_murphy = FALSE;
1591 player->artwork_element = player->element_nr;
1593 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1594 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1596 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1598 player->actual_frame_counter = 0;
1600 player->step_counter = 0;
1602 player->last_move_dir = MV_NONE;
1604 player->is_waiting = FALSE;
1605 player->is_moving = FALSE;
1606 player->is_auto_moving = FALSE;
1607 player->is_digging = FALSE;
1608 player->is_snapping = FALSE;
1609 player->is_collecting = FALSE;
1610 player->is_pushing = FALSE;
1611 player->is_switching = FALSE;
1612 player->is_dropping = FALSE;
1614 player->is_bored = FALSE;
1615 player->is_sleeping = FALSE;
1617 player->cannot_move = FALSE;
1619 player->frame_counter_bored = -1;
1620 player->frame_counter_sleeping = -1;
1622 player->anim_delay_counter = 0;
1623 player->post_delay_counter = 0;
1625 player->action_waiting = ACTION_DEFAULT;
1626 player->last_action_waiting = ACTION_DEFAULT;
1627 player->special_action_bored = ACTION_DEFAULT;
1628 player->special_action_sleeping = ACTION_DEFAULT;
1630 /* set number of special actions for bored and sleeping animation */
1631 player->num_special_action_bored =
1632 get_num_special_action(player->artwork_element,
1633 ACTION_BORING_1, ACTION_BORING_LAST);
1634 player->num_special_action_sleeping =
1635 get_num_special_action(player->artwork_element,
1636 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
1638 player->switch_x = -1;
1639 player->switch_y = -1;
1641 player->drop_x = -1;
1642 player->drop_y = -1;
1644 player->show_envelope = 0;
1646 player->move_delay = game.initial_move_delay;
1647 player->move_delay_value = game.initial_move_delay_value;
1649 player->move_delay_value_next = -1;
1651 player->move_delay_reset_counter = 0;
1653 player->push_delay = -1; /* initialized when pushing starts */
1654 player->push_delay_value = game.initial_push_delay_value;
1656 player->drop_delay = 0;
1658 player->last_jx = player->last_jy = 0;
1659 player->jx = player->jy = 0;
1661 player->shield_normal_time_left = 0;
1662 player->shield_deadly_time_left = 0;
1664 player->inventory_infinite_element = EL_UNDEFINED;
1665 player->inventory_size = 0;
1667 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1668 SnapField(player, 0, 0);
1670 player->LevelSolved = FALSE;
1671 player->GameOver = FALSE;
1674 network_player_action_received = FALSE;
1676 #if defined(NETWORK_AVALIABLE)
1677 /* initial null action */
1678 if (network_playing)
1679 SendToServer_MovePlayer(MV_NONE);
1688 TimeLeft = level.time;
1691 ScreenMovDir = MV_NONE;
1695 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1697 AllPlayersGone = FALSE;
1699 game.yamyam_content_nr = 0;
1700 game.magic_wall_active = FALSE;
1701 game.magic_wall_time_left = 0;
1702 game.light_time_left = 0;
1703 game.timegate_time_left = 0;
1704 game.switchgate_pos = 0;
1705 game.wind_direction = level.wind_direction_initial;
1706 game.gravity = level.initial_gravity;
1707 game.explosions_delayed = TRUE;
1709 game.lenses_time_left = 0;
1710 game.magnify_time_left = 0;
1712 game.envelope_active = FALSE;
1714 for (i = 0; i < NUM_BELTS; i++)
1716 game.belt_dir[i] = MV_NONE;
1717 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1720 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1721 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1723 for (x = 0; x < lev_fieldx; x++)
1725 for (y = 0; y < lev_fieldy; y++)
1727 Feld[x][y] = level.field[x][y];
1728 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1729 ChangeDelay[x][y] = 0;
1730 ChangePage[x][y] = -1;
1731 #if USE_NEW_CUSTOM_VALUE
1732 CustomValue[x][y] = 0; /* initialized in InitField() */
1734 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1736 WasJustMoving[x][y] = 0;
1737 WasJustFalling[x][y] = 0;
1738 CheckCollision[x][y] = 0;
1740 Pushed[x][y] = FALSE;
1743 ChangeEvent[x][y] = -1;
1745 ExplodePhase[x][y] = 0;
1746 ExplodeDelay[x][y] = 0;
1747 ExplodeField[x][y] = EX_TYPE_NONE;
1749 RunnerVisit[x][y] = 0;
1750 PlayerVisit[x][y] = 0;
1753 GfxRandom[x][y] = INIT_GFX_RANDOM();
1754 GfxElement[x][y] = EL_UNDEFINED;
1755 GfxAction[x][y] = ACTION_DEFAULT;
1756 GfxDir[x][y] = MV_NONE;
1760 for (y = 0; y < lev_fieldy; y++)
1762 for (x = 0; x < lev_fieldx; x++)
1764 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1766 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1768 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1771 InitField(x, y, TRUE);
1777 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1778 emulate_sb ? EMU_SOKOBAN :
1779 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1781 #if USE_NEW_ALL_SLIPPERY
1782 /* initialize type of slippery elements */
1783 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1785 if (!IS_CUSTOM_ELEMENT(i))
1787 /* default: elements slip down either to the left or right randomly */
1788 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
1790 /* SP style elements prefer to slip down on the left side */
1791 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
1792 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
1794 /* BD style elements prefer to slip down on the left side */
1795 if (game.emulation == EMU_BOULDERDASH)
1796 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
1801 /* initialize explosion and ignition delay */
1802 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1804 if (!IS_CUSTOM_ELEMENT(i))
1807 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
1808 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
1809 game.emulation == EMU_SUPAPLEX ? 3 : 2);
1810 int last_phase = (num_phase + 1) * delay;
1811 int half_phase = (num_phase / 2) * delay;
1813 element_info[i].explosion_delay = last_phase - 1;
1814 element_info[i].ignition_delay = half_phase;
1816 if (i == EL_BLACK_ORB)
1817 element_info[i].ignition_delay = 1;
1821 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
1822 element_info[i].explosion_delay = 1;
1824 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
1825 element_info[i].ignition_delay = 1;
1829 /* correct non-moving belts to start moving left */
1830 for (i = 0; i < NUM_BELTS; i++)
1831 if (game.belt_dir[i] == MV_NONE)
1832 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1834 /* check if any connected player was not found in playfield */
1835 for (i = 0; i < MAX_PLAYERS; i++)
1837 struct PlayerInfo *player = &stored_player[i];
1839 if (player->connected && !player->present)
1841 for (j = 0; j < MAX_PLAYERS; j++)
1843 struct PlayerInfo *some_player = &stored_player[j];
1844 int jx = some_player->jx, jy = some_player->jy;
1846 /* assign first free player found that is present in the playfield */
1847 if (some_player->present && !some_player->connected)
1849 player->present = TRUE;
1850 player->active = TRUE;
1852 some_player->present = FALSE;
1853 some_player->active = FALSE;
1856 player->element_nr = some_player->element_nr;
1859 player->artwork_element = some_player->artwork_element;
1861 player->block_last_field = some_player->block_last_field;
1862 player->block_delay_adjustment = some_player->block_delay_adjustment;
1864 StorePlayer[jx][jy] = player->element_nr;
1865 player->jx = player->last_jx = jx;
1866 player->jy = player->last_jy = jy;
1876 /* when playing a tape, eliminate all players which do not participate */
1878 for (i = 0; i < MAX_PLAYERS; i++)
1880 if (stored_player[i].active && !tape.player_participates[i])
1882 struct PlayerInfo *player = &stored_player[i];
1883 int jx = player->jx, jy = player->jy;
1885 player->active = FALSE;
1886 StorePlayer[jx][jy] = 0;
1887 Feld[jx][jy] = EL_EMPTY;
1891 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1893 /* when in single player mode, eliminate all but the first active player */
1895 for (i = 0; i < MAX_PLAYERS; i++)
1897 if (stored_player[i].active)
1899 for (j = i + 1; j < MAX_PLAYERS; j++)
1901 if (stored_player[j].active)
1903 struct PlayerInfo *player = &stored_player[j];
1904 int jx = player->jx, jy = player->jy;
1906 player->active = FALSE;
1907 player->present = FALSE;
1909 StorePlayer[jx][jy] = 0;
1910 Feld[jx][jy] = EL_EMPTY;
1917 /* when recording the game, store which players take part in the game */
1920 for (i = 0; i < MAX_PLAYERS; i++)
1921 if (stored_player[i].active)
1922 tape.player_participates[i] = TRUE;
1927 for (i = 0; i < MAX_PLAYERS; i++)
1929 struct PlayerInfo *player = &stored_player[i];
1931 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1936 if (local_player == player)
1937 printf("Player %d is local player.\n", i+1);
1941 if (BorderElement == EL_EMPTY)
1944 SBX_Right = lev_fieldx - SCR_FIELDX;
1946 SBY_Lower = lev_fieldy - SCR_FIELDY;
1951 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1953 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1956 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1957 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1959 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1960 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1962 /* if local player not found, look for custom element that might create
1963 the player (make some assumptions about the right custom element) */
1964 if (!local_player->present)
1966 int start_x = 0, start_y = 0;
1967 int found_rating = 0;
1968 int found_element = EL_UNDEFINED;
1969 int player_nr = local_player->index_nr;
1971 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1973 int element = Feld[x][y];
1978 if (level.use_start_element[player_nr] &&
1979 level.start_element[player_nr] == element &&
1986 found_element = element;
1989 if (!IS_CUSTOM_ELEMENT(element))
1992 if (CAN_CHANGE(element))
1994 for (i = 0; i < element_info[element].num_change_pages; i++)
1996 /* check for player created from custom element as single target */
1997 content = element_info[element].change_page[i].target_element;
1998 is_player = ELEM_IS_PLAYER(content);
2000 if (is_player && (found_rating < 3 || element < found_element))
2006 found_element = element;
2011 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2013 /* check for player created from custom element as explosion content */
2014 content = element_info[element].content.e[xx][yy];
2015 is_player = ELEM_IS_PLAYER(content);
2017 if (is_player && (found_rating < 2 || element < found_element))
2019 start_x = x + xx - 1;
2020 start_y = y + yy - 1;
2023 found_element = element;
2026 if (!CAN_CHANGE(element))
2029 for (i = 0; i < element_info[element].num_change_pages; i++)
2031 /* check for player created from custom element as extended target */
2033 element_info[element].change_page[i].target_content.e[xx][yy];
2035 is_player = ELEM_IS_PLAYER(content);
2037 if (is_player && (found_rating < 1 || element < found_element))
2039 start_x = x + xx - 1;
2040 start_y = y + yy - 1;
2043 found_element = element;
2049 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2050 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2053 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2054 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2059 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2060 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2061 local_player->jx - MIDPOSX);
2063 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2064 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2065 local_player->jy - MIDPOSY);
2068 if (!game.restart_level)
2069 CloseDoor(DOOR_CLOSE_1);
2071 /* !!! FIX THIS (START) !!! */
2072 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2074 InitGameEngine_EM();
2081 /* after drawing the level, correct some elements */
2082 if (game.timegate_time_left == 0)
2083 CloseAllOpenTimegates();
2085 if (setup.soft_scrolling)
2086 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2088 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2091 /* !!! FIX THIS (END) !!! */
2093 if (!game.restart_level)
2095 /* copy default game door content to main double buffer */
2096 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2097 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2100 DrawGameDoorValues();
2102 if (!game.restart_level)
2106 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2107 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2108 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2112 /* copy actual game door content to door double buffer for OpenDoor() */
2113 BlitBitmap(drawto, bitmap_db_door,
2114 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2116 OpenDoor(DOOR_OPEN_ALL);
2118 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2120 if (setup.sound_music)
2123 KeyboardAutoRepeatOffUnlessAutoplay();
2127 for (i = 0; i < MAX_PLAYERS; i++)
2128 printf("Player %d %sactive.\n",
2129 i + 1, (stored_player[i].active ? "" : "not "));
2133 game.restart_level = FALSE;
2136 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2138 /* this is used for non-R'n'D game engines to update certain engine values */
2140 /* needed to determine if sounds are played within the visible screen area */
2141 scroll_x = actual_scroll_x;
2142 scroll_y = actual_scroll_y;
2145 void InitMovDir(int x, int y)
2147 int i, element = Feld[x][y];
2148 static int xy[4][2] =
2155 static int direction[3][4] =
2157 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2158 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2159 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2168 Feld[x][y] = EL_BUG;
2169 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2172 case EL_SPACESHIP_RIGHT:
2173 case EL_SPACESHIP_UP:
2174 case EL_SPACESHIP_LEFT:
2175 case EL_SPACESHIP_DOWN:
2176 Feld[x][y] = EL_SPACESHIP;
2177 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2180 case EL_BD_BUTTERFLY_RIGHT:
2181 case EL_BD_BUTTERFLY_UP:
2182 case EL_BD_BUTTERFLY_LEFT:
2183 case EL_BD_BUTTERFLY_DOWN:
2184 Feld[x][y] = EL_BD_BUTTERFLY;
2185 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2188 case EL_BD_FIREFLY_RIGHT:
2189 case EL_BD_FIREFLY_UP:
2190 case EL_BD_FIREFLY_LEFT:
2191 case EL_BD_FIREFLY_DOWN:
2192 Feld[x][y] = EL_BD_FIREFLY;
2193 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2196 case EL_PACMAN_RIGHT:
2198 case EL_PACMAN_LEFT:
2199 case EL_PACMAN_DOWN:
2200 Feld[x][y] = EL_PACMAN;
2201 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2204 case EL_SP_SNIKSNAK:
2205 MovDir[x][y] = MV_UP;
2208 case EL_SP_ELECTRON:
2209 MovDir[x][y] = MV_LEFT;
2216 Feld[x][y] = EL_MOLE;
2217 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2221 if (IS_CUSTOM_ELEMENT(element))
2223 struct ElementInfo *ei = &element_info[element];
2224 int move_direction_initial = ei->move_direction_initial;
2225 int move_pattern = ei->move_pattern;
2227 if (move_direction_initial == MV_START_PREVIOUS)
2229 if (MovDir[x][y] != MV_NONE)
2232 move_direction_initial = MV_START_AUTOMATIC;
2235 if (move_direction_initial == MV_START_RANDOM)
2236 MovDir[x][y] = 1 << RND(4);
2237 else if (move_direction_initial & MV_ANY_DIRECTION)
2238 MovDir[x][y] = move_direction_initial;
2239 else if (move_pattern == MV_ALL_DIRECTIONS ||
2240 move_pattern == MV_TURNING_LEFT ||
2241 move_pattern == MV_TURNING_RIGHT ||
2242 move_pattern == MV_TURNING_LEFT_RIGHT ||
2243 move_pattern == MV_TURNING_RIGHT_LEFT ||
2244 move_pattern == MV_TURNING_RANDOM)
2245 MovDir[x][y] = 1 << RND(4);
2246 else if (move_pattern == MV_HORIZONTAL)
2247 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2248 else if (move_pattern == MV_VERTICAL)
2249 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2250 else if (move_pattern & MV_ANY_DIRECTION)
2251 MovDir[x][y] = element_info[element].move_pattern;
2252 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2253 move_pattern == MV_ALONG_RIGHT_SIDE)
2255 /* use random direction as default start direction */
2256 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2257 MovDir[x][y] = 1 << RND(4);
2259 for (i = 0; i < NUM_DIRECTIONS; i++)
2261 int x1 = x + xy[i][0];
2262 int y1 = y + xy[i][1];
2264 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2266 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2267 MovDir[x][y] = direction[0][i];
2269 MovDir[x][y] = direction[1][i];
2278 MovDir[x][y] = 1 << RND(4);
2280 if (element != EL_BUG &&
2281 element != EL_SPACESHIP &&
2282 element != EL_BD_BUTTERFLY &&
2283 element != EL_BD_FIREFLY)
2286 for (i = 0; i < NUM_DIRECTIONS; i++)
2288 int x1 = x + xy[i][0];
2289 int y1 = y + xy[i][1];
2291 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2293 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2295 MovDir[x][y] = direction[0][i];
2298 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2299 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2301 MovDir[x][y] = direction[1][i];
2310 GfxDir[x][y] = MovDir[x][y];
2313 void InitAmoebaNr(int x, int y)
2316 int group_nr = AmoebeNachbarNr(x, y);
2320 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2322 if (AmoebaCnt[i] == 0)
2330 AmoebaNr[x][y] = group_nr;
2331 AmoebaCnt[group_nr]++;
2332 AmoebaCnt2[group_nr]++;
2338 boolean raise_level = FALSE;
2340 if (local_player->MovPos)
2343 if (tape.auto_play) /* tape might already be stopped here */
2344 tape.auto_play_level_solved = TRUE;
2346 local_player->LevelSolved = FALSE;
2348 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2352 if (!tape.playing && setup.sound_loops)
2353 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2354 SND_CTRL_PLAY_LOOP);
2356 while (TimeLeft > 0)
2358 if (!tape.playing && !setup.sound_loops)
2359 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2361 if (TimeLeft > 100 && TimeLeft % 10 == 0)
2364 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2369 RaiseScore(level.score[SC_TIME_BONUS]);
2372 DrawGameValue_Time(TimeLeft);
2380 if (!tape.playing && setup.sound_loops)
2381 StopSound(SND_GAME_LEVELTIME_BONUS);
2383 else if (level.time == 0) /* level without time limit */
2385 if (!tape.playing && setup.sound_loops)
2386 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2387 SND_CTRL_PLAY_LOOP);
2389 while (TimePlayed < 999)
2391 if (!tape.playing && !setup.sound_loops)
2392 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2394 if (TimePlayed < 900 && TimePlayed % 10 == 0)
2397 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2402 RaiseScore(level.score[SC_TIME_BONUS]);
2405 DrawGameValue_Time(TimePlayed);
2413 if (!tape.playing && setup.sound_loops)
2414 StopSound(SND_GAME_LEVELTIME_BONUS);
2417 /* close exit door after last player */
2418 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
2419 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2420 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2422 int element = Feld[ExitX][ExitY];
2424 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2425 EL_SP_EXIT_CLOSING);
2427 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2430 /* player disappears */
2431 if (ExitX >= 0 && ExitY >= 0)
2432 DrawLevelField(ExitX, ExitY);
2439 CloseDoor(DOOR_CLOSE_1);
2444 SaveTape(tape.level_nr); /* Ask to save tape */
2447 if (level_nr == leveldir_current->handicap_level)
2449 leveldir_current->handicap_level++;
2450 SaveLevelSetup_SeriesInfo();
2453 if (level_editor_test_game)
2454 local_player->score = -1; /* no highscore when playing from editor */
2455 else if (level_nr < leveldir_current->last_level)
2456 raise_level = TRUE; /* advance to next level */
2458 if ((hi_pos = NewHiScore()) >= 0)
2460 game_status = GAME_MODE_SCORES;
2461 DrawHallOfFame(hi_pos);
2470 game_status = GAME_MODE_MAIN;
2487 LoadScore(level_nr);
2489 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2490 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2493 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2495 if (local_player->score > highscore[k].Score)
2497 /* player has made it to the hall of fame */
2499 if (k < MAX_SCORE_ENTRIES - 1)
2501 int m = MAX_SCORE_ENTRIES - 1;
2504 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2505 if (!strcmp(setup.player_name, highscore[l].Name))
2507 if (m == k) /* player's new highscore overwrites his old one */
2511 for (l = m; l > k; l--)
2513 strcpy(highscore[l].Name, highscore[l - 1].Name);
2514 highscore[l].Score = highscore[l - 1].Score;
2521 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2522 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2523 highscore[k].Score = local_player->score;
2529 else if (!strncmp(setup.player_name, highscore[k].Name,
2530 MAX_PLAYER_NAME_LEN))
2531 break; /* player already there with a higher score */
2537 SaveScore(level_nr);
2542 inline static int getElementMoveStepsize(int x, int y)
2544 int element = Feld[x][y];
2545 int direction = MovDir[x][y];
2546 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2547 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2548 int horiz_move = (dx != 0);
2549 int sign = (horiz_move ? dx : dy);
2550 int step = sign * element_info[element].move_stepsize;
2552 /* special values for move stepsize for spring and things on conveyor belt */
2556 if (element == EL_SPRING)
2557 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2558 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
2559 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2560 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2562 if (CAN_FALL(element) &&
2563 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2564 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2565 else if (element == EL_SPRING)
2566 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2573 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2575 if (player->GfxAction != action || player->GfxDir != dir)
2578 printf("Player frame reset! (%d => %d, %d => %d)\n",
2579 player->GfxAction, action, player->GfxDir, dir);
2582 player->GfxAction = action;
2583 player->GfxDir = dir;
2585 player->StepFrame = 0;
2589 static void ResetRandomAnimationValue(int x, int y)
2591 GfxRandom[x][y] = INIT_GFX_RANDOM();
2594 static void ResetGfxAnimation(int x, int y)
2597 GfxAction[x][y] = ACTION_DEFAULT;
2598 GfxDir[x][y] = MovDir[x][y];
2601 void InitMovingField(int x, int y, int direction)
2603 int element = Feld[x][y];
2604 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2605 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2609 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2610 ResetGfxAnimation(x, y);
2612 MovDir[x][y] = direction;
2613 GfxDir[x][y] = direction;
2614 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
2615 ACTION_FALLING : ACTION_MOVING);
2617 /* this is needed for CEs with property "can move" / "not moving" */
2619 if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
2621 if (Feld[newx][newy] == EL_EMPTY)
2622 Feld[newx][newy] = EL_BLOCKED;
2624 MovDir[newx][newy] = MovDir[x][y];
2626 #if USE_NEW_CUSTOM_VALUE
2627 CustomValue[newx][newy] = CustomValue[x][y];
2630 GfxFrame[newx][newy] = GfxFrame[x][y];
2631 GfxRandom[newx][newy] = GfxRandom[x][y];
2632 GfxAction[newx][newy] = GfxAction[x][y];
2633 GfxDir[newx][newy] = GfxDir[x][y];
2637 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2639 int direction = MovDir[x][y];
2640 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2641 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2647 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2649 int oldx = x, oldy = y;
2650 int direction = MovDir[x][y];
2652 if (direction == MV_LEFT)
2654 else if (direction == MV_RIGHT)
2656 else if (direction == MV_UP)
2658 else if (direction == MV_DOWN)
2661 *comes_from_x = oldx;
2662 *comes_from_y = oldy;
2665 int MovingOrBlocked2Element(int x, int y)
2667 int element = Feld[x][y];
2669 if (element == EL_BLOCKED)
2673 Blocked2Moving(x, y, &oldx, &oldy);
2674 return Feld[oldx][oldy];
2680 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2682 /* like MovingOrBlocked2Element(), but if element is moving
2683 and (x,y) is the field the moving element is just leaving,
2684 return EL_BLOCKED instead of the element value */
2685 int element = Feld[x][y];
2687 if (IS_MOVING(x, y))
2689 if (element == EL_BLOCKED)
2693 Blocked2Moving(x, y, &oldx, &oldy);
2694 return Feld[oldx][oldy];
2703 static void RemoveField(int x, int y)
2705 Feld[x][y] = EL_EMPTY;
2711 #if USE_NEW_CUSTOM_VALUE
2712 CustomValue[x][y] = 0;
2716 ChangeDelay[x][y] = 0;
2717 ChangePage[x][y] = -1;
2718 Pushed[x][y] = FALSE;
2721 ExplodeField[x][y] = EX_TYPE_NONE;
2724 GfxElement[x][y] = EL_UNDEFINED;
2725 GfxAction[x][y] = ACTION_DEFAULT;
2726 GfxDir[x][y] = MV_NONE;
2729 void RemoveMovingField(int x, int y)
2731 int oldx = x, oldy = y, newx = x, newy = y;
2732 int element = Feld[x][y];
2733 int next_element = EL_UNDEFINED;
2735 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2738 if (IS_MOVING(x, y))
2740 Moving2Blocked(x, y, &newx, &newy);
2742 if (Feld[newx][newy] != EL_BLOCKED)
2744 /* element is moving, but target field is not free (blocked), but
2745 already occupied by something different (example: acid pool);
2746 in this case, only remove the moving field, but not the target */
2748 RemoveField(oldx, oldy);
2750 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2752 DrawLevelField(oldx, oldy);
2757 else if (element == EL_BLOCKED)
2759 Blocked2Moving(x, y, &oldx, &oldy);
2760 if (!IS_MOVING(oldx, oldy))
2764 if (element == EL_BLOCKED &&
2765 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2766 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2767 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2768 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2769 next_element = get_next_element(Feld[oldx][oldy]);
2771 RemoveField(oldx, oldy);
2772 RemoveField(newx, newy);
2774 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2776 if (next_element != EL_UNDEFINED)
2777 Feld[oldx][oldy] = next_element;
2779 DrawLevelField(oldx, oldy);
2780 DrawLevelField(newx, newy);
2783 void DrawDynamite(int x, int y)
2785 int sx = SCREENX(x), sy = SCREENY(y);
2786 int graphic = el2img(Feld[x][y]);
2789 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2792 if (IS_WALKABLE_INSIDE(Back[x][y]))
2796 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2797 else if (Store[x][y])
2798 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2800 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2802 if (Back[x][y] || Store[x][y])
2803 DrawGraphicThruMask(sx, sy, graphic, frame);
2805 DrawGraphic(sx, sy, graphic, frame);
2808 void CheckDynamite(int x, int y)
2810 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2814 if (MovDelay[x][y] != 0)
2817 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2823 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2828 void DrawRelocatePlayer(struct PlayerInfo *player)
2830 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2831 boolean no_delay = (tape.warp_forward);
2832 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2833 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2834 int jx = player->jx;
2835 int jy = player->jy;
2837 if (level.instant_relocation)
2839 int offset = (setup.scroll_delay ? 3 : 0);
2841 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
2843 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2844 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2845 local_player->jx - MIDPOSX);
2847 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2848 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2849 local_player->jy - MIDPOSY);
2853 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
2854 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
2855 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
2857 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
2858 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
2859 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
2861 /* don't scroll over playfield boundaries */
2862 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2863 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2865 /* don't scroll over playfield boundaries */
2866 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2867 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2870 RedrawPlayfield(TRUE, 0,0,0,0);
2874 int scroll_xx = -999, scroll_yy = -999;
2876 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2878 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2881 int fx = FX, fy = FY;
2883 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2884 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2885 local_player->jx - MIDPOSX);
2887 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2888 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2889 local_player->jy - MIDPOSY);
2891 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2892 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2894 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2900 fx += dx * TILEX / 2;
2901 fy += dy * TILEY / 2;
2903 ScrollLevel(dx, dy);
2906 /* scroll in two steps of half tile size to make things smoother */
2907 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2909 Delay(wait_delay_value);
2911 /* scroll second step to align at full tile size */
2913 Delay(wait_delay_value);
2918 Delay(wait_delay_value);
2922 void RelocatePlayer(int jx, int jy, int el_player_raw)
2924 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
2925 int player_nr = GET_PLAYER_NR(el_player);
2926 struct PlayerInfo *player = &stored_player[player_nr];
2927 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2928 boolean no_delay = (tape.warp_forward);
2929 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2930 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2931 int old_jx = player->jx;
2932 int old_jy = player->jy;
2933 int old_element = Feld[old_jx][old_jy];
2934 int element = Feld[jx][jy];
2935 boolean player_relocated = (old_jx != jx || old_jy != jy);
2937 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
2938 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
2939 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
2940 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
2941 int leave_side_horiz = move_dir_horiz;
2942 int leave_side_vert = move_dir_vert;
2943 int enter_side = enter_side_horiz | enter_side_vert;
2944 int leave_side = leave_side_horiz | leave_side_vert;
2946 if (player->GameOver) /* do not reanimate dead player */
2949 if (!player_relocated) /* no need to relocate the player */
2952 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
2954 RemoveField(jx, jy); /* temporarily remove newly placed player */
2955 DrawLevelField(jx, jy);
2958 if (player->present)
2960 while (player->MovPos)
2962 ScrollPlayer(player, SCROLL_GO_ON);
2963 ScrollScreen(NULL, SCROLL_GO_ON);
2965 AdvanceFrameAndPlayerCounters(player->index_nr);
2970 Delay(wait_delay_value);
2973 DrawPlayer(player); /* needed here only to cleanup last field */
2974 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2976 player->is_moving = FALSE;
2979 if (IS_CUSTOM_ELEMENT(old_element))
2980 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
2982 player->index_bit, leave_side);
2984 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
2986 player->index_bit, leave_side);
2988 Feld[jx][jy] = el_player;
2989 InitPlayerField(jx, jy, el_player, TRUE);
2991 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
2993 Feld[jx][jy] = element;
2994 InitField(jx, jy, FALSE);
2997 if (player == local_player) /* only visually relocate local player */
2998 DrawRelocatePlayer(player);
3000 TestIfPlayerTouchesBadThing(jx, jy);
3001 TestIfPlayerTouchesCustomElement(jx, jy);
3003 if (IS_CUSTOM_ELEMENT(element))
3004 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3005 player->index_bit, enter_side);
3007 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
3008 player->index_bit, enter_side);
3011 void Explode(int ex, int ey, int phase, int mode)
3017 /* !!! eliminate this variable !!! */
3018 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3020 if (game.explosions_delayed)
3022 ExplodeField[ex][ey] = mode;
3026 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3028 int center_element = Feld[ex][ey];
3031 /* --- This is only really needed (and now handled) in "Impact()". --- */
3032 /* do not explode moving elements that left the explode field in time */
3033 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3034 center_element == EL_EMPTY &&
3035 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3039 if (mode == EX_TYPE_NORMAL ||
3040 mode == EX_TYPE_CENTER ||
3041 mode == EX_TYPE_CROSS)
3042 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
3044 /* remove things displayed in background while burning dynamite */
3045 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3048 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3050 /* put moving element to center field (and let it explode there) */
3051 center_element = MovingOrBlocked2Element(ex, ey);
3052 RemoveMovingField(ex, ey);
3053 Feld[ex][ey] = center_element;
3056 last_phase = element_info[center_element].explosion_delay + 1;
3058 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3060 int xx = x - ex + 1;
3061 int yy = y - ey + 1;
3064 if (!IN_LEV_FIELD(x, y) ||
3065 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3066 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3069 element = Feld[x][y];
3071 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3073 element = MovingOrBlocked2Element(x, y);
3075 if (!IS_EXPLOSION_PROOF(element))
3076 RemoveMovingField(x, y);
3079 /* indestructible elements can only explode in center (but not flames) */
3080 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3081 mode == EX_TYPE_BORDER)) ||
3082 element == EL_FLAMES)
3085 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3086 behaviour, for example when touching a yamyam that explodes to rocks
3087 with active deadly shield, a rock is created under the player !!! */
3088 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3090 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3091 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3092 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3094 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3097 if (IS_ACTIVE_BOMB(element))
3099 /* re-activate things under the bomb like gate or penguin */
3100 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3107 /* save walkable background elements while explosion on same tile */
3108 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3109 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3110 Back[x][y] = element;
3112 /* ignite explodable elements reached by other explosion */
3113 if (element == EL_EXPLOSION)
3114 element = Store2[x][y];
3116 if (AmoebaNr[x][y] &&
3117 (element == EL_AMOEBA_FULL ||
3118 element == EL_BD_AMOEBA ||
3119 element == EL_AMOEBA_GROWING))
3121 AmoebaCnt[AmoebaNr[x][y]]--;
3122 AmoebaCnt2[AmoebaNr[x][y]]--;
3127 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3129 switch(StorePlayer[ex][ey])
3132 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3135 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3138 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3142 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3146 if (PLAYERINFO(ex, ey)->use_murphy)
3147 Store[x][y] = EL_EMPTY;
3149 else if (center_element == EL_MOLE)
3150 Store[x][y] = EL_EMERALD_RED;
3151 else if (center_element == EL_PENGUIN)
3152 Store[x][y] = EL_EMERALD_PURPLE;
3153 else if (center_element == EL_BUG)
3154 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3155 else if (center_element == EL_BD_BUTTERFLY)
3156 Store[x][y] = EL_BD_DIAMOND;
3157 else if (center_element == EL_SP_ELECTRON)
3158 Store[x][y] = EL_SP_INFOTRON;
3159 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3160 Store[x][y] = level.amoeba_content;
3161 else if (center_element == EL_YAMYAM)
3162 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3163 else if (IS_CUSTOM_ELEMENT(center_element) &&
3164 element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3165 Store[x][y] = element_info[center_element].content.e[xx][yy];
3166 else if (element == EL_WALL_EMERALD)
3167 Store[x][y] = EL_EMERALD;
3168 else if (element == EL_WALL_DIAMOND)
3169 Store[x][y] = EL_DIAMOND;
3170 else if (element == EL_WALL_BD_DIAMOND)
3171 Store[x][y] = EL_BD_DIAMOND;
3172 else if (element == EL_WALL_EMERALD_YELLOW)
3173 Store[x][y] = EL_EMERALD_YELLOW;
3174 else if (element == EL_WALL_EMERALD_RED)
3175 Store[x][y] = EL_EMERALD_RED;
3176 else if (element == EL_WALL_EMERALD_PURPLE)
3177 Store[x][y] = EL_EMERALD_PURPLE;
3178 else if (element == EL_WALL_PEARL)
3179 Store[x][y] = EL_PEARL;
3180 else if (element == EL_WALL_CRYSTAL)
3181 Store[x][y] = EL_CRYSTAL;
3182 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3183 Store[x][y] = element_info[element].content.e[1][1];
3185 Store[x][y] = EL_EMPTY;
3187 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3188 center_element == EL_AMOEBA_TO_DIAMOND)
3189 Store2[x][y] = element;
3191 Feld[x][y] = EL_EXPLOSION;
3192 GfxElement[x][y] = center_element;
3194 ExplodePhase[x][y] = 1;
3195 ExplodeDelay[x][y] = last_phase;
3200 if (center_element == EL_YAMYAM)
3201 game.yamyam_content_nr =
3202 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3214 GfxFrame[x][y] = 0; /* restart explosion animation */
3216 last_phase = ExplodeDelay[x][y];
3218 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3222 /* activate this even in non-DEBUG version until cause for crash in
3223 getGraphicAnimationFrame() (see below) is found and eliminated */
3228 if (GfxElement[x][y] == EL_UNDEFINED)
3231 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3232 printf("Explode(): This should never happen!\n");
3235 GfxElement[x][y] = EL_EMPTY;
3239 border_element = Store2[x][y];
3240 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3241 border_element = StorePlayer[x][y];
3243 if (phase == element_info[border_element].ignition_delay ||
3244 phase == last_phase)
3246 boolean border_explosion = FALSE;
3248 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3249 !PLAYER_EXPLOSION_PROTECTED(x, y))
3251 KillPlayerUnlessExplosionProtected(x, y);
3252 border_explosion = TRUE;
3254 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3256 Feld[x][y] = Store2[x][y];
3259 border_explosion = TRUE;
3261 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3263 AmoebeUmwandeln(x, y);
3265 border_explosion = TRUE;
3268 /* if an element just explodes due to another explosion (chain-reaction),
3269 do not immediately end the new explosion when it was the last frame of
3270 the explosion (as it would be done in the following "if"-statement!) */
3271 if (border_explosion && phase == last_phase)
3275 if (phase == last_phase)
3279 element = Feld[x][y] = Store[x][y];
3280 Store[x][y] = Store2[x][y] = 0;
3281 GfxElement[x][y] = EL_UNDEFINED;
3283 /* player can escape from explosions and might therefore be still alive */
3284 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3285 element <= EL_PLAYER_IS_EXPLODING_4)
3286 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
3288 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
3289 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
3290 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
3293 /* restore probably existing indestructible background element */
3294 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3295 element = Feld[x][y] = Back[x][y];
3298 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3299 GfxDir[x][y] = MV_NONE;
3300 ChangeDelay[x][y] = 0;
3301 ChangePage[x][y] = -1;
3303 #if USE_NEW_CUSTOM_VALUE
3304 CustomValue[x][y] = 0;
3307 InitField_WithBug2(x, y, FALSE);
3309 DrawLevelField(x, y);
3311 TestIfElementTouchesCustomElement(x, y);
3313 if (GFX_CRUMBLED(element))
3314 DrawLevelFieldCrumbledSandNeighbours(x, y);
3316 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3317 StorePlayer[x][y] = 0;
3319 if (ELEM_IS_PLAYER(element))
3320 RelocatePlayer(x, y, element);
3322 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3324 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3325 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3328 DrawLevelFieldCrumbledSand(x, y);
3330 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3332 DrawLevelElement(x, y, Back[x][y]);
3333 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3335 else if (IS_WALKABLE_UNDER(Back[x][y]))
3337 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3338 DrawLevelElementThruMask(x, y, Back[x][y]);
3340 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3341 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3345 void DynaExplode(int ex, int ey)
3348 int dynabomb_element = Feld[ex][ey];
3349 int dynabomb_size = 1;
3350 boolean dynabomb_xl = FALSE;
3351 struct PlayerInfo *player;
3352 static int xy[4][2] =
3360 if (IS_ACTIVE_BOMB(dynabomb_element))
3362 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3363 dynabomb_size = player->dynabomb_size;
3364 dynabomb_xl = player->dynabomb_xl;
3365 player->dynabombs_left++;
3368 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3370 for (i = 0; i < NUM_DIRECTIONS; i++)
3372 for (j = 1; j <= dynabomb_size; j++)
3374 int x = ex + j * xy[i][0];
3375 int y = ey + j * xy[i][1];
3378 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3381 element = Feld[x][y];
3383 /* do not restart explosions of fields with active bombs */
3384 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3387 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3389 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3390 !IS_DIGGABLE(element) && !dynabomb_xl)
3396 void Bang(int x, int y)
3398 int element = MovingOrBlocked2Element(x, y);
3400 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3402 struct PlayerInfo *player = PLAYERINFO(x, y);
3404 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
3405 player->element_nr);
3412 case EL_BD_BUTTERFLY:
3415 case EL_DARK_YAMYAM:
3419 RaiseScoreElement(element);
3420 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3422 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3423 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3424 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3425 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3426 case EL_DYNABOMB_INCREASE_NUMBER:
3427 case EL_DYNABOMB_INCREASE_SIZE:
3428 case EL_DYNABOMB_INCREASE_POWER:
3433 case EL_LAMP_ACTIVE:
3434 case EL_AMOEBA_TO_DIAMOND:
3435 if (IS_PLAYER(x, y))
3436 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3438 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3441 if (element_info[element].explosion_type == EXPLODES_CROSS)
3442 Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS);
3443 else if (element_info[element].explosion_type == EXPLODES_1X1)
3444 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3446 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3450 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
3453 void SplashAcid(int x, int y)
3455 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3456 (!IN_LEV_FIELD(x - 1, y - 2) ||
3457 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3458 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3460 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3461 (!IN_LEV_FIELD(x + 1, y - 2) ||
3462 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3463 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3465 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3468 static void InitBeltMovement()
3470 static int belt_base_element[4] =
3472 EL_CONVEYOR_BELT_1_LEFT,
3473 EL_CONVEYOR_BELT_2_LEFT,
3474 EL_CONVEYOR_BELT_3_LEFT,
3475 EL_CONVEYOR_BELT_4_LEFT
3477 static int belt_base_active_element[4] =
3479 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3480 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3481 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3482 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3487 /* set frame order for belt animation graphic according to belt direction */
3488 for (i = 0; i < NUM_BELTS; i++)
3492 for (j = 0; j < NUM_BELT_PARTS; j++)
3494 int element = belt_base_active_element[belt_nr] + j;
3495 int graphic = el2img(element);
3497 if (game.belt_dir[i] == MV_LEFT)
3498 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3500 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3504 for (y = 0; y < lev_fieldy; y++)
3506 for (x = 0; x < lev_fieldx; x++)
3508 int element = Feld[x][y];
3510 for (i = 0; i < NUM_BELTS; i++)
3512 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
3514 int e_belt_nr = getBeltNrFromBeltElement(element);
3517 if (e_belt_nr == belt_nr)
3519 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3521 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3529 static void ToggleBeltSwitch(int x, int y)
3531 static int belt_base_element[4] =
3533 EL_CONVEYOR_BELT_1_LEFT,
3534 EL_CONVEYOR_BELT_2_LEFT,
3535 EL_CONVEYOR_BELT_3_LEFT,
3536 EL_CONVEYOR_BELT_4_LEFT
3538 static int belt_base_active_element[4] =
3540 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3541 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3542 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3543 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3545 static int belt_base_switch_element[4] =
3547 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3548 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3549 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3550 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3552 static int belt_move_dir[4] =
3560 int element = Feld[x][y];
3561 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3562 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3563 int belt_dir = belt_move_dir[belt_dir_nr];
3566 if (!IS_BELT_SWITCH(element))
3569 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3570 game.belt_dir[belt_nr] = belt_dir;
3572 if (belt_dir_nr == 3)
3575 /* set frame order for belt animation graphic according to belt direction */
3576 for (i = 0; i < NUM_BELT_PARTS; i++)
3578 int element = belt_base_active_element[belt_nr] + i;
3579 int graphic = el2img(element);
3581 if (belt_dir == MV_LEFT)
3582 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3584 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3587 for (yy = 0; yy < lev_fieldy; yy++)
3589 for (xx = 0; xx < lev_fieldx; xx++)
3591 int element = Feld[xx][yy];
3593 if (IS_BELT_SWITCH(element))
3595 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3597 if (e_belt_nr == belt_nr)
3599 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3600 DrawLevelField(xx, yy);
3603 else if (IS_BELT(element) && belt_dir != MV_NONE)
3605 int e_belt_nr = getBeltNrFromBeltElement(element);
3607 if (e_belt_nr == belt_nr)
3609 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3611 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3612 DrawLevelField(xx, yy);
3615 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
3617 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3619 if (e_belt_nr == belt_nr)
3621 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3623 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3624 DrawLevelField(xx, yy);
3631 static void ToggleSwitchgateSwitch(int x, int y)
3635 game.switchgate_pos = !game.switchgate_pos;
3637 for (yy = 0; yy < lev_fieldy; yy++)
3639 for (xx = 0; xx < lev_fieldx; xx++)
3641 int element = Feld[xx][yy];
3643 if (element == EL_SWITCHGATE_SWITCH_UP ||
3644 element == EL_SWITCHGATE_SWITCH_DOWN)
3646 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3647 DrawLevelField(xx, yy);
3649 else if (element == EL_SWITCHGATE_OPEN ||
3650 element == EL_SWITCHGATE_OPENING)
3652 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3654 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3656 else if (element == EL_SWITCHGATE_CLOSED ||
3657 element == EL_SWITCHGATE_CLOSING)
3659 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3661 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3667 static int getInvisibleActiveFromInvisibleElement(int element)
3669 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3670 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
3671 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
3675 static int getInvisibleFromInvisibleActiveElement(int element)
3677 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3678 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
3679 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
3683 static void RedrawAllLightSwitchesAndInvisibleElements()
3687 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
3689 int element = Feld[x][y];
3691 if (element == EL_LIGHT_SWITCH &&
3692 game.light_time_left > 0)
3694 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3695 DrawLevelField(x, y);
3697 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3698 game.light_time_left == 0)
3700 Feld[x][y] = EL_LIGHT_SWITCH;
3701 DrawLevelField(x, y);
3703 else if (element == EL_EMC_DRIPPER &&
3704 game.light_time_left > 0)
3706 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
3707 DrawLevelField(x, y);
3709 else if (element == EL_EMC_DRIPPER_ACTIVE &&
3710 game.light_time_left == 0)
3712 Feld[x][y] = EL_EMC_DRIPPER;
3713 DrawLevelField(x, y);
3715 else if (element == EL_INVISIBLE_STEELWALL ||
3716 element == EL_INVISIBLE_WALL ||
3717 element == EL_INVISIBLE_SAND)
3719 if (game.light_time_left > 0)
3720 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3722 DrawLevelField(x, y);
3724 /* uncrumble neighbour fields, if needed */
3725 if (element == EL_INVISIBLE_SAND)
3726 DrawLevelFieldCrumbledSandNeighbours(x, y);
3728 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3729 element == EL_INVISIBLE_WALL_ACTIVE ||
3730 element == EL_INVISIBLE_SAND_ACTIVE)
3732 if (game.light_time_left == 0)
3733 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3735 DrawLevelField(x, y);
3737 /* re-crumble neighbour fields, if needed */
3738 if (element == EL_INVISIBLE_SAND)
3739 DrawLevelFieldCrumbledSandNeighbours(x, y);
3744 static void RedrawAllInvisibleElementsForLenses()
3748 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
3750 int element = Feld[x][y];
3752 if (element == EL_EMC_DRIPPER &&
3753 game.lenses_time_left > 0)
3755 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
3756 DrawLevelField(x, y);
3758 else if (element == EL_EMC_DRIPPER_ACTIVE &&
3759 game.lenses_time_left == 0)
3761 Feld[x][y] = EL_EMC_DRIPPER;
3762 DrawLevelField(x, y);
3764 else if (element == EL_INVISIBLE_STEELWALL ||
3765 element == EL_INVISIBLE_WALL ||
3766 element == EL_INVISIBLE_SAND)
3768 if (game.lenses_time_left > 0)
3769 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3771 DrawLevelField(x, y);
3773 /* uncrumble neighbour fields, if needed */
3774 if (element == EL_INVISIBLE_SAND)
3775 DrawLevelFieldCrumbledSandNeighbours(x, y);
3777 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3778 element == EL_INVISIBLE_WALL_ACTIVE ||
3779 element == EL_INVISIBLE_SAND_ACTIVE)
3781 if (game.lenses_time_left == 0)
3782 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3784 DrawLevelField(x, y);
3786 /* re-crumble neighbour fields, if needed */
3787 if (element == EL_INVISIBLE_SAND)
3788 DrawLevelFieldCrumbledSandNeighbours(x, y);
3793 static void RedrawAllInvisibleElementsForMagnifier()
3797 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
3799 int element = Feld[x][y];
3801 if (element == EL_EMC_FAKE_GRASS &&
3802 game.magnify_time_left > 0)
3804 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
3805 DrawLevelField(x, y);
3807 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
3808 game.magnify_time_left == 0)
3810 Feld[x][y] = EL_EMC_FAKE_GRASS;
3811 DrawLevelField(x, y);
3813 else if (IS_GATE_GRAY(element) &&
3814 game.magnify_time_left > 0)
3816 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
3817 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
3818 IS_EM_GATE_GRAY(element) ?
3819 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
3820 IS_EMC_GATE_GRAY(element) ?
3821 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
3823 DrawLevelField(x, y);
3825 else if (IS_GATE_GRAY_ACTIVE(element) &&
3826 game.magnify_time_left == 0)
3828 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
3829 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
3830 IS_EM_GATE_GRAY_ACTIVE(element) ?
3831 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
3832 IS_EMC_GATE_GRAY_ACTIVE(element) ?
3833 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
3835 DrawLevelField(x, y);
3840 static void ToggleLightSwitch(int x, int y)
3842 int element = Feld[x][y];
3844 game.light_time_left =
3845 (element == EL_LIGHT_SWITCH ?
3846 level.time_light * FRAMES_PER_SECOND : 0);
3848 RedrawAllLightSwitchesAndInvisibleElements();
3851 static void ActivateTimegateSwitch(int x, int y)
3855 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3857 for (yy = 0; yy < lev_fieldy; yy++)
3859 for (xx = 0; xx < lev_fieldx; xx++)
3861 int element = Feld[xx][yy];
3863 if (element == EL_TIMEGATE_CLOSED ||
3864 element == EL_TIMEGATE_CLOSING)
3866 Feld[xx][yy] = EL_TIMEGATE_OPENING;
3867 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3871 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3873 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3874 DrawLevelField(xx, yy);
3881 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3884 void Impact(int x, int y)
3886 boolean last_line = (y == lev_fieldy - 1);
3887 boolean object_hit = FALSE;
3888 boolean impact = (last_line || object_hit);
3889 int element = Feld[x][y];
3890 int smashed = EL_STEELWALL;
3892 if (!last_line) /* check if element below was hit */
3894 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3897 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3898 MovDir[x][y + 1] != MV_DOWN ||
3899 MovPos[x][y + 1] <= TILEY / 2));
3901 /* do not smash moving elements that left the smashed field in time */
3902 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
3903 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3907 smashed = MovingOrBlocked2Element(x, y + 1);
3909 impact = (last_line || object_hit);
3912 if (!last_line && smashed == EL_ACID) /* element falls into acid */
3914 SplashAcid(x, y + 1);
3918 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
3919 /* only reset graphic animation if graphic really changes after impact */
3921 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3923 ResetGfxAnimation(x, y);
3924 DrawLevelField(x, y);
3927 if (impact && CAN_EXPLODE_IMPACT(element))
3932 else if (impact && element == EL_PEARL)
3934 ResetGfxAnimation(x, y);
3936 Feld[x][y] = EL_PEARL_BREAKING;
3937 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3940 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
3942 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3947 if (impact && element == EL_AMOEBA_DROP)
3949 if (object_hit && IS_PLAYER(x, y + 1))
3950 KillPlayerUnlessEnemyProtected(x, y + 1);
3951 else if (object_hit && smashed == EL_PENGUIN)
3955 Feld[x][y] = EL_AMOEBA_GROWING;
3956 Store[x][y] = EL_AMOEBA_WET;
3958 ResetRandomAnimationValue(x, y);
3963 if (object_hit) /* check which object was hit */
3965 if (CAN_PASS_MAGIC_WALL(element) &&
3966 (smashed == EL_MAGIC_WALL ||
3967 smashed == EL_BD_MAGIC_WALL))
3970 int activated_magic_wall =
3971 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3972 EL_BD_MAGIC_WALL_ACTIVE);
3974 /* activate magic wall / mill */
3975 for (yy = 0; yy < lev_fieldy; yy++)
3976 for (xx = 0; xx < lev_fieldx; xx++)
3977 if (Feld[xx][yy] == smashed)
3978 Feld[xx][yy] = activated_magic_wall;
3980 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3981 game.magic_wall_active = TRUE;
3983 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
3984 SND_MAGIC_WALL_ACTIVATING :
3985 SND_BD_MAGIC_WALL_ACTIVATING));
3988 if (IS_PLAYER(x, y + 1))
3990 if (CAN_SMASH_PLAYER(element))
3992 KillPlayerUnlessEnemyProtected(x, y + 1);
3996 else if (smashed == EL_PENGUIN)
3998 if (CAN_SMASH_PLAYER(element))
4004 else if (element == EL_BD_DIAMOND)
4006 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4012 else if (((element == EL_SP_INFOTRON ||
4013 element == EL_SP_ZONK) &&
4014 (smashed == EL_SP_SNIKSNAK ||
4015 smashed == EL_SP_ELECTRON ||
4016 smashed == EL_SP_DISK_ORANGE)) ||
4017 (element == EL_SP_INFOTRON &&
4018 smashed == EL_SP_DISK_YELLOW))
4023 else if (CAN_SMASH_EVERYTHING(element))
4025 if (IS_CLASSIC_ENEMY(smashed) ||
4026 CAN_EXPLODE_SMASHED(smashed))
4031 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4033 if (smashed == EL_LAMP ||
4034 smashed == EL_LAMP_ACTIVE)
4039 else if (smashed == EL_NUT)
4041 Feld[x][y + 1] = EL_NUT_BREAKING;
4042 PlayLevelSound(x, y, SND_NUT_BREAKING);
4043 RaiseScoreElement(EL_NUT);
4046 else if (smashed == EL_PEARL)
4048 ResetGfxAnimation(x, y);
4050 Feld[x][y + 1] = EL_PEARL_BREAKING;
4051 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4054 else if (smashed == EL_DIAMOND)
4056 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4057 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4060 else if (IS_BELT_SWITCH(smashed))
4062 ToggleBeltSwitch(x, y + 1);
4064 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4065 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4067 ToggleSwitchgateSwitch(x, y + 1);
4069 else if (smashed == EL_LIGHT_SWITCH ||
4070 smashed == EL_LIGHT_SWITCH_ACTIVE)
4072 ToggleLightSwitch(x, y + 1);
4077 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4080 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4082 CheckElementChangeBySide(x, y + 1, smashed, element,
4083 CE_SWITCHED, CH_SIDE_TOP);
4084 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
4090 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4095 /* play sound of magic wall / mill */
4097 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4098 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4100 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4101 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4102 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4103 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4108 /* play sound of object that hits the ground */
4109 if (last_line || object_hit)
4110 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4113 inline static void TurnRoundExt(int x, int y)
4125 { 0, 0 }, { 0, 0 }, { 0, 0 },
4130 int left, right, back;
4134 { MV_DOWN, MV_UP, MV_RIGHT },
4135 { MV_UP, MV_DOWN, MV_LEFT },
4137 { MV_LEFT, MV_RIGHT, MV_DOWN },
4141 { MV_RIGHT, MV_LEFT, MV_UP }
4144 int element = Feld[x][y];
4145 int move_pattern = element_info[element].move_pattern;
4147 int old_move_dir = MovDir[x][y];
4148 int left_dir = turn[old_move_dir].left;
4149 int right_dir = turn[old_move_dir].right;
4150 int back_dir = turn[old_move_dir].back;
4152 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
4153 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
4154 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
4155 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
4157 int left_x = x + left_dx, left_y = y + left_dy;
4158 int right_x = x + right_dx, right_y = y + right_dy;
4159 int move_x = x + move_dx, move_y = y + move_dy;
4163 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4165 TestIfBadThingTouchesOtherBadThing(x, y);
4167 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4168 MovDir[x][y] = right_dir;
4169 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4170 MovDir[x][y] = left_dir;
4172 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4174 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4177 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4179 TestIfBadThingTouchesOtherBadThing(x, y);
4181 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4182 MovDir[x][y] = left_dir;
4183 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4184 MovDir[x][y] = right_dir;
4186 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4188 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4191 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4193 TestIfBadThingTouchesOtherBadThing(x, y);
4195 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4196 MovDir[x][y] = left_dir;
4197 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4198 MovDir[x][y] = right_dir;
4200 if (MovDir[x][y] != old_move_dir)
4203 else if (element == EL_YAMYAM)
4205 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4206 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4208 if (can_turn_left && can_turn_right)
4209 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4210 else if (can_turn_left)
4211 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4212 else if (can_turn_right)
4213 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4215 MovDir[x][y] = back_dir;
4217 MovDelay[x][y] = 16 + 16 * RND(3);
4219 else if (element == EL_DARK_YAMYAM)
4221 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4223 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4226 if (can_turn_left && can_turn_right)
4227 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4228 else if (can_turn_left)
4229 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4230 else if (can_turn_right)
4231 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4233 MovDir[x][y] = back_dir;
4235 MovDelay[x][y] = 16 + 16 * RND(3);
4237 else if (element == EL_PACMAN)
4239 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4240 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4242 if (can_turn_left && can_turn_right)
4243 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4244 else if (can_turn_left)
4245 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4246 else if (can_turn_right)
4247 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4249 MovDir[x][y] = back_dir;
4251 MovDelay[x][y] = 6 + RND(40);
4253 else if (element == EL_PIG)
4255 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4256 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4257 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4258 boolean should_turn_left, should_turn_right, should_move_on;
4260 int rnd = RND(rnd_value);
4262 should_turn_left = (can_turn_left &&
4264 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4265 y + back_dy + left_dy)));
4266 should_turn_right = (can_turn_right &&
4268 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4269 y + back_dy + right_dy)));
4270 should_move_on = (can_move_on &&
4273 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4274 y + move_dy + left_dy) ||
4275 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4276 y + move_dy + right_dy)));
4278 if (should_turn_left || should_turn_right || should_move_on)
4280 if (should_turn_left && should_turn_right && should_move_on)
4281 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4282 rnd < 2 * rnd_value / 3 ? right_dir :
4284 else if (should_turn_left && should_turn_right)
4285 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4286 else if (should_turn_left && should_move_on)
4287 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4288 else if (should_turn_right && should_move_on)
4289 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4290 else if (should_turn_left)
4291 MovDir[x][y] = left_dir;
4292 else if (should_turn_right)
4293 MovDir[x][y] = right_dir;
4294 else if (should_move_on)
4295 MovDir[x][y] = old_move_dir;
4297 else if (can_move_on && rnd > rnd_value / 8)
4298 MovDir[x][y] = old_move_dir;
4299 else if (can_turn_left && can_turn_right)
4300 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4301 else if (can_turn_left && rnd > rnd_value / 8)
4302 MovDir[x][y] = left_dir;
4303 else if (can_turn_right && rnd > rnd_value/8)
4304 MovDir[x][y] = right_dir;
4306 MovDir[x][y] = back_dir;
4308 xx = x + move_xy[MovDir[x][y]].x;
4309 yy = y + move_xy[MovDir[x][y]].y;
4311 if (!IN_LEV_FIELD(xx, yy) ||
4312 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4313 MovDir[x][y] = old_move_dir;
4317 else if (element == EL_DRAGON)
4319 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4320 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4321 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4323 int rnd = RND(rnd_value);
4325 if (can_move_on && rnd > rnd_value / 8)
4326 MovDir[x][y] = old_move_dir;
4327 else if (can_turn_left && can_turn_right)
4328 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4329 else if (can_turn_left && rnd > rnd_value / 8)
4330 MovDir[x][y] = left_dir;
4331 else if (can_turn_right && rnd > rnd_value / 8)
4332 MovDir[x][y] = right_dir;
4334 MovDir[x][y] = back_dir;
4336 xx = x + move_xy[MovDir[x][y]].x;
4337 yy = y + move_xy[MovDir[x][y]].y;
4339 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4340 MovDir[x][y] = old_move_dir;
4344 else if (element == EL_MOLE)
4346 boolean can_move_on =
4347 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4348 IS_AMOEBOID(Feld[move_x][move_y]) ||
4349 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4352 boolean can_turn_left =
4353 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4354 IS_AMOEBOID(Feld[left_x][left_y])));
4356 boolean can_turn_right =
4357 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4358 IS_AMOEBOID(Feld[right_x][right_y])));
4360 if (can_turn_left && can_turn_right)
4361 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4362 else if (can_turn_left)
4363 MovDir[x][y] = left_dir;
4365 MovDir[x][y] = right_dir;
4368 if (MovDir[x][y] != old_move_dir)
4371 else if (element == EL_BALLOON)
4373 MovDir[x][y] = game.wind_direction;
4376 else if (element == EL_SPRING)
4378 if (MovDir[x][y] & MV_HORIZONTAL &&
4379 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4380 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4381 MovDir[x][y] = MV_NONE;
4385 else if (element == EL_ROBOT ||
4386 element == EL_SATELLITE ||
4387 element == EL_PENGUIN)
4389 int attr_x = -1, attr_y = -1;
4400 for (i = 0; i < MAX_PLAYERS; i++)
4402 struct PlayerInfo *player = &stored_player[i];
4403 int jx = player->jx, jy = player->jy;
4405 if (!player->active)
4409 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4417 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4418 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4419 game.engine_version < VERSION_IDENT(3,1,0,0)))
4425 if (element == EL_PENGUIN)
4428 static int xy[4][2] =
4436 for (i = 0; i < NUM_DIRECTIONS; i++)
4438 int ex = x + xy[i][0];
4439 int ey = y + xy[i][1];
4441 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4450 MovDir[x][y] = MV_NONE;
4452 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4453 else if (attr_x > x)
4454 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4456 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4457 else if (attr_y > y)
4458 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4460 if (element == EL_ROBOT)
4464 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4465 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4466 Moving2Blocked(x, y, &newx, &newy);
4468 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4469 MovDelay[x][y] = 8 + 8 * !RND(3);
4471 MovDelay[x][y] = 16;
4473 else if (element == EL_PENGUIN)
4479 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4481 boolean first_horiz = RND(2);
4482 int new_move_dir = MovDir[x][y];
4485 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4486 Moving2Blocked(x, y, &newx, &newy);
4488 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4492 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4493 Moving2Blocked(x, y, &newx, &newy);
4495 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4498 MovDir[x][y] = old_move_dir;
4502 else /* (element == EL_SATELLITE) */
4508 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4510 boolean first_horiz = RND(2);
4511 int new_move_dir = MovDir[x][y];
4514 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4515 Moving2Blocked(x, y, &newx, &newy);
4517 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4521 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4522 Moving2Blocked(x, y, &newx, &newy);
4524 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4527 MovDir[x][y] = old_move_dir;
4532 else if (move_pattern == MV_TURNING_LEFT ||
4533 move_pattern == MV_TURNING_RIGHT ||
4534 move_pattern == MV_TURNING_LEFT_RIGHT ||
4535 move_pattern == MV_TURNING_RIGHT_LEFT ||
4536 move_pattern == MV_TURNING_RANDOM ||
4537 move_pattern == MV_ALL_DIRECTIONS)
4539 boolean can_turn_left =
4540 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4541 boolean can_turn_right =
4542 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4544 if (element_info[element].move_stepsize == 0) /* "not moving" */
4547 if (move_pattern == MV_TURNING_LEFT)
4548 MovDir[x][y] = left_dir;
4549 else if (move_pattern == MV_TURNING_RIGHT)
4550 MovDir[x][y] = right_dir;
4551 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4552 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4553 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4554 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4555 else if (move_pattern == MV_TURNING_RANDOM)
4556 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4557 can_turn_right && !can_turn_left ? right_dir :
4558 RND(2) ? left_dir : right_dir);
4559 else if (can_turn_left && can_turn_right)
4560 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4561 else if (can_turn_left)
4562 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4563 else if (can_turn_right)
4564 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4566 MovDir[x][y] = back_dir;
4568 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4570 else if (move_pattern == MV_HORIZONTAL ||
4571 move_pattern == MV_VERTICAL)
4573 if (move_pattern & old_move_dir)
4574 MovDir[x][y] = back_dir;
4575 else if (move_pattern == MV_HORIZONTAL)
4576 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4577 else if (move_pattern == MV_VERTICAL)
4578 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4580 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4582 else if (move_pattern & MV_ANY_DIRECTION)
4584 MovDir[x][y] = move_pattern;
4585 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4587 else if (move_pattern & MV_WIND_DIRECTION)
4589 MovDir[x][y] = game.wind_direction;
4590 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4592 else if (move_pattern == MV_ALONG_LEFT_SIDE)
4594 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4595 MovDir[x][y] = left_dir;
4596 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4597 MovDir[x][y] = right_dir;
4599 if (MovDir[x][y] != old_move_dir)
4600 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4602 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4604 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4605 MovDir[x][y] = right_dir;
4606 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4607 MovDir[x][y] = left_dir;
4609 if (MovDir[x][y] != old_move_dir)
4610 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4612 else if (move_pattern == MV_TOWARDS_PLAYER ||
4613 move_pattern == MV_AWAY_FROM_PLAYER)
4615 int attr_x = -1, attr_y = -1;
4617 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4628 for (i = 0; i < MAX_PLAYERS; i++)
4630 struct PlayerInfo *player = &stored_player[i];
4631 int jx = player->jx, jy = player->jy;
4633 if (!player->active)
4637 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4645 MovDir[x][y] = MV_NONE;
4647 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4648 else if (attr_x > x)
4649 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4651 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4652 else if (attr_y > y)
4653 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4655 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4657 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4659 boolean first_horiz = RND(2);
4660 int new_move_dir = MovDir[x][y];
4662 if (element_info[element].move_stepsize == 0) /* "not moving" */
4664 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
4665 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4671 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4672 Moving2Blocked(x, y, &newx, &newy);
4674 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4678 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4679 Moving2Blocked(x, y, &newx, &newy);
4681 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4684 MovDir[x][y] = old_move_dir;
4687 else if (move_pattern == MV_WHEN_PUSHED ||
4688 move_pattern == MV_WHEN_DROPPED)
4690 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4691 MovDir[x][y] = MV_NONE;
4695 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4697 static int test_xy[7][2] =
4707 static int test_dir[7] =
4717 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4718 int move_preference = -1000000; /* start with very low preference */
4719 int new_move_dir = MV_NONE;
4720 int start_test = RND(4);
4723 for (i = 0; i < NUM_DIRECTIONS; i++)
4725 int move_dir = test_dir[start_test + i];
4726 int move_dir_preference;
4728 xx = x + test_xy[start_test + i][0];
4729 yy = y + test_xy[start_test + i][1];
4731 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4732 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4734 new_move_dir = move_dir;
4739 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4742 move_dir_preference = -1 * RunnerVisit[xx][yy];
4743 if (hunter_mode && PlayerVisit[xx][yy] > 0)
4744 move_dir_preference = PlayerVisit[xx][yy];
4746 if (move_dir_preference > move_preference)
4748 /* prefer field that has not been visited for the longest time */
4749 move_preference = move_dir_preference;
4750 new_move_dir = move_dir;
4752 else if (move_dir_preference == move_preference &&
4753 move_dir == old_move_dir)
4755 /* prefer last direction when all directions are preferred equally */
4756 move_preference = move_dir_preference;
4757 new_move_dir = move_dir;
4761 MovDir[x][y] = new_move_dir;
4762 if (old_move_dir != new_move_dir)
4763 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4767 static void TurnRound(int x, int y)
4769 int direction = MovDir[x][y];
4773 GfxDir[x][y] = MovDir[x][y];
4775 if (direction != MovDir[x][y])
4779 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4782 static boolean JustBeingPushed(int x, int y)
4786 for (i = 0; i < MAX_PLAYERS; i++)
4788 struct PlayerInfo *player = &stored_player[i];
4790 if (player->active && player->is_pushing && player->MovPos)
4792 int next_jx = player->jx + (player->jx - player->last_jx);
4793 int next_jy = player->jy + (player->jy - player->last_jy);
4795 if (x == next_jx && y == next_jy)
4803 void StartMoving(int x, int y)
4805 boolean started_moving = FALSE; /* some elements can fall _and_ move */
4806 int element = Feld[x][y];
4811 if (MovDelay[x][y] == 0)
4812 GfxAction[x][y] = ACTION_DEFAULT;
4814 if (CAN_FALL(element) && y < lev_fieldy - 1)
4816 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
4817 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
4818 if (JustBeingPushed(x, y))
4821 if (element == EL_QUICKSAND_FULL)
4823 if (IS_FREE(x, y + 1))
4825 InitMovingField(x, y, MV_DOWN);
4826 started_moving = TRUE;
4828 Feld[x][y] = EL_QUICKSAND_EMPTYING;
4829 Store[x][y] = EL_ROCK;
4831 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4833 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4835 if (!MovDelay[x][y])
4836 MovDelay[x][y] = TILEY + 1;
4845 Feld[x][y] = EL_QUICKSAND_EMPTY;
4846 Feld[x][y + 1] = EL_QUICKSAND_FULL;
4847 Store[x][y + 1] = Store[x][y];
4850 PlayLevelSoundAction(x, y, ACTION_FILLING);
4853 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4854 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4856 InitMovingField(x, y, MV_DOWN);
4857 started_moving = TRUE;
4859 Feld[x][y] = EL_QUICKSAND_FILLING;
4860 Store[x][y] = element;
4862 PlayLevelSoundAction(x, y, ACTION_FILLING);
4864 else if (element == EL_MAGIC_WALL_FULL)
4866 if (IS_FREE(x, y + 1))
4868 InitMovingField(x, y, MV_DOWN);
4869 started_moving = TRUE;
4871 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4872 Store[x][y] = EL_CHANGED(Store[x][y]);
4874 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4876 if (!MovDelay[x][y])
4877 MovDelay[x][y] = TILEY/4 + 1;
4886 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4887 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4888 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4892 else if (element == EL_BD_MAGIC_WALL_FULL)
4894 if (IS_FREE(x, y + 1))
4896 InitMovingField(x, y, MV_DOWN);
4897 started_moving = TRUE;
4899 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4900 Store[x][y] = EL_CHANGED2(Store[x][y]);
4902 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4904 if (!MovDelay[x][y])
4905 MovDelay[x][y] = TILEY/4 + 1;
4914 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
4915 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
4916 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
4920 else if (CAN_PASS_MAGIC_WALL(element) &&
4921 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4922 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4924 InitMovingField(x, y, MV_DOWN);
4925 started_moving = TRUE;
4928 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
4929 EL_BD_MAGIC_WALL_FILLING);
4930 Store[x][y] = element;
4932 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
4934 SplashAcid(x, y + 1);
4936 InitMovingField(x, y, MV_DOWN);
4937 started_moving = TRUE;
4939 Store[x][y] = EL_ACID;
4941 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
4942 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
4944 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
4945 CAN_FALL(element) && WasJustFalling[x][y] &&
4946 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
4948 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
4949 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
4950 (Feld[x][y + 1] == EL_BLOCKED)))
4952 /* this is needed for a special case not covered by calling "Impact()"
4953 from "ContinueMoving()": if an element moves to a tile directly below
4954 another element which was just falling on that tile (which was empty
4955 in the previous frame), the falling element above would just stop
4956 instead of smashing the element below (in previous version, the above
4957 element was just checked for "moving" instead of "falling", resulting
4958 in incorrect smashes caused by horizontal movement of the above
4959 element; also, the case of the player being the element to smash was
4960 simply not covered here... :-/ ) */
4962 CheckCollision[x][y] = 0;
4966 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
4968 if (MovDir[x][y] == MV_NONE)
4970 InitMovingField(x, y, MV_DOWN);
4971 started_moving = TRUE;
4974 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
4976 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
4977 MovDir[x][y] = MV_DOWN;
4979 InitMovingField(x, y, MV_DOWN);
4980 started_moving = TRUE;
4982 else if (element == EL_AMOEBA_DROP)
4984 Feld[x][y] = EL_AMOEBA_GROWING;
4985 Store[x][y] = EL_AMOEBA_WET;
4987 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4988 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4989 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4990 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4992 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
4993 (IS_FREE(x - 1, y + 1) ||
4994 Feld[x - 1][y + 1] == EL_ACID));
4995 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4996 (IS_FREE(x + 1, y + 1) ||
4997 Feld[x + 1][y + 1] == EL_ACID));
4998 boolean can_fall_any = (can_fall_left || can_fall_right);
4999 boolean can_fall_both = (can_fall_left && can_fall_right);
5000 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5002 #if USE_NEW_ALL_SLIPPERY
5003 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
5005 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5006 can_fall_right = FALSE;
5007 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5008 can_fall_left = FALSE;
5009 else if (slippery_type == SLIPPERY_ONLY_LEFT)
5010 can_fall_right = FALSE;
5011 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5012 can_fall_left = FALSE;
5014 can_fall_any = (can_fall_left || can_fall_right);
5015 can_fall_both = FALSE;
5018 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5020 if (slippery_type == SLIPPERY_ONLY_LEFT)
5021 can_fall_right = FALSE;
5022 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5023 can_fall_left = FALSE;
5024 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5025 can_fall_right = FALSE;
5026 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5027 can_fall_left = FALSE;
5029 can_fall_any = (can_fall_left || can_fall_right);
5030 can_fall_both = (can_fall_left && can_fall_right);
5034 #if USE_NEW_ALL_SLIPPERY
5036 #if USE_NEW_SP_SLIPPERY
5037 /* !!! better use the same properties as for custom elements here !!! */
5038 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5039 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5041 can_fall_right = FALSE; /* slip down on left side */
5042 can_fall_both = FALSE;
5047 #if USE_NEW_ALL_SLIPPERY
5050 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5051 can_fall_right = FALSE; /* slip down on left side */
5053 can_fall_left = !(can_fall_right = RND(2));
5055 can_fall_both = FALSE;
5060 if (game.emulation == EMU_BOULDERDASH ||
5061 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5062 can_fall_right = FALSE; /* slip down on left side */
5064 can_fall_left = !(can_fall_right = RND(2));
5066 can_fall_both = FALSE;
5072 /* if not determined otherwise, prefer left side for slipping down */
5073 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5074 started_moving = TRUE;
5078 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5080 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5083 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5084 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5085 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5086 int belt_dir = game.belt_dir[belt_nr];
5088 if ((belt_dir == MV_LEFT && left_is_free) ||
5089 (belt_dir == MV_RIGHT && right_is_free))
5091 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5093 InitMovingField(x, y, belt_dir);
5094 started_moving = TRUE;
5096 Pushed[x][y] = TRUE;
5097 Pushed[nextx][y] = TRUE;
5099 GfxAction[x][y] = ACTION_DEFAULT;
5103 MovDir[x][y] = 0; /* if element was moving, stop it */
5108 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5110 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
5112 if (CAN_MOVE(element) && !started_moving)
5115 int move_pattern = element_info[element].move_pattern;
5120 if (MovDir[x][y] == MV_NONE)
5122 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5123 x, y, element, element_info[element].token_name);
5124 printf("StartMoving(): This should never happen!\n");
5129 Moving2Blocked(x, y, &newx, &newy);
5131 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5134 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5135 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5137 WasJustMoving[x][y] = 0;
5138 CheckCollision[x][y] = 0;
5140 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5142 if (Feld[x][y] != element) /* element has changed */
5146 if (!MovDelay[x][y]) /* start new movement phase */
5148 /* all objects that can change their move direction after each step
5149 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5151 if (element != EL_YAMYAM &&
5152 element != EL_DARK_YAMYAM &&
5153 element != EL_PACMAN &&
5154 !(move_pattern & MV_ANY_DIRECTION) &&
5155 move_pattern != MV_TURNING_LEFT &&
5156 move_pattern != MV_TURNING_RIGHT &&
5157 move_pattern != MV_TURNING_LEFT_RIGHT &&
5158 move_pattern != MV_TURNING_RIGHT_LEFT &&
5159 move_pattern != MV_TURNING_RANDOM)
5163 if (MovDelay[x][y] && (element == EL_BUG ||
5164 element == EL_SPACESHIP ||
5165 element == EL_SP_SNIKSNAK ||
5166 element == EL_SP_ELECTRON ||
5167 element == EL_MOLE))
5168 DrawLevelField(x, y);
5172 if (MovDelay[x][y]) /* wait some time before next movement */
5176 if (element == EL_ROBOT ||
5177 element == EL_YAMYAM ||
5178 element == EL_DARK_YAMYAM)
5180 DrawLevelElementAnimationIfNeeded(x, y, element);
5181 PlayLevelSoundAction(x, y, ACTION_WAITING);
5183 else if (element == EL_SP_ELECTRON)
5184 DrawLevelElementAnimationIfNeeded(x, y, element);
5185 else if (element == EL_DRAGON)
5188 int dir = MovDir[x][y];
5189 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5190 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5191 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5192 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5193 dir == MV_UP ? IMG_FLAMES_1_UP :
5194 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5195 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5197 GfxAction[x][y] = ACTION_ATTACKING;
5199 if (IS_PLAYER(x, y))
5200 DrawPlayerField(x, y);
5202 DrawLevelField(x, y);
5204 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5206 for (i = 1; i <= 3; i++)
5208 int xx = x + i * dx;
5209 int yy = y + i * dy;
5210 int sx = SCREENX(xx);
5211 int sy = SCREENY(yy);
5212 int flame_graphic = graphic + (i - 1);
5214 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5219 int flamed = MovingOrBlocked2Element(xx, yy);
5223 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5225 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5226 RemoveMovingField(xx, yy);
5228 RemoveField(xx, yy);
5230 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5233 RemoveMovingField(xx, yy);
5236 ChangeDelay[xx][yy] = 0;
5238 Feld[xx][yy] = EL_FLAMES;
5240 if (IN_SCR_FIELD(sx, sy))
5242 DrawLevelFieldCrumbledSand(xx, yy);
5243 DrawGraphic(sx, sy, flame_graphic, frame);
5248 if (Feld[xx][yy] == EL_FLAMES)
5249 Feld[xx][yy] = EL_EMPTY;
5250 DrawLevelField(xx, yy);
5255 if (MovDelay[x][y]) /* element still has to wait some time */
5257 PlayLevelSoundAction(x, y, ACTION_WAITING);
5263 /* now make next step */
5265 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5267 if (DONT_COLLIDE_WITH(element) &&
5268 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5269 !PLAYER_ENEMY_PROTECTED(newx, newy))
5271 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
5276 else if (CAN_MOVE_INTO_ACID(element) &&
5277 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5278 (MovDir[x][y] == MV_DOWN ||
5279 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5281 SplashAcid(newx, newy);
5282 Store[x][y] = EL_ACID;
5284 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5286 if (Feld[newx][newy] == EL_EXIT_OPEN)
5289 DrawLevelField(x, y);
5291 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5292 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5293 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5295 local_player->friends_still_needed--;
5296 if (!local_player->friends_still_needed &&
5297 !local_player->GameOver && AllPlayersGone)
5298 local_player->LevelSolved = local_player->GameOver = TRUE;
5302 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5304 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
5305 DrawLevelField(newx, newy);
5307 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
5309 else if (!IS_FREE(newx, newy))
5311 GfxAction[x][y] = ACTION_WAITING;
5313 if (IS_PLAYER(x, y))
5314 DrawPlayerField(x, y);
5316 DrawLevelField(x, y);
5321 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5323 if (IS_FOOD_PIG(Feld[newx][newy]))
5325 if (IS_MOVING(newx, newy))
5326 RemoveMovingField(newx, newy);
5329 Feld[newx][newy] = EL_EMPTY;
5330 DrawLevelField(newx, newy);
5333 PlayLevelSound(x, y, SND_PIG_DIGGING);
5335 else if (!IS_FREE(newx, newy))
5337 if (IS_PLAYER(x, y))
5338 DrawPlayerField(x, y);
5340 DrawLevelField(x, y);
5345 else if (IS_CUSTOM_ELEMENT(element) &&
5346 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5348 int new_element = Feld[newx][newy];
5350 if (!IS_FREE(newx, newy))
5352 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5353 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5356 /* no element can dig solid indestructible elements */
5357 if (IS_INDESTRUCTIBLE(new_element) &&
5358 !IS_DIGGABLE(new_element) &&
5359 !IS_COLLECTIBLE(new_element))
5362 if (AmoebaNr[newx][newy] &&
5363 (new_element == EL_AMOEBA_FULL ||
5364 new_element == EL_BD_AMOEBA ||
5365 new_element == EL_AMOEBA_GROWING))
5367 AmoebaCnt[AmoebaNr[newx][newy]]--;
5368 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5371 if (IS_MOVING(newx, newy))
5372 RemoveMovingField(newx, newy);
5375 RemoveField(newx, newy);
5376 DrawLevelField(newx, newy);
5379 /* if digged element was about to explode, prevent the explosion */
5380 ExplodeField[newx][newy] = EX_TYPE_NONE;
5382 PlayLevelSoundAction(x, y, action);
5385 Store[newx][newy] = EL_EMPTY;
5386 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5388 int move_leave_element = element_info[element].move_leave_element;
5390 /* this makes it possible to leave the removed element again */
5391 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
5392 new_element : move_leave_element);
5395 if (move_pattern & MV_MAZE_RUNNER_STYLE)
5397 RunnerVisit[x][y] = FrameCounter;
5398 PlayerVisit[x][y] /= 8; /* expire player visit path */
5401 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5403 if (!IS_FREE(newx, newy))
5405 if (IS_PLAYER(x, y))
5406 DrawPlayerField(x, y);
5408 DrawLevelField(x, y);
5414 boolean wanna_flame = !RND(10);
5415 int dx = newx - x, dy = newy - y;
5416 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5417 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5418 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5419 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5420 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5421 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5424 IS_CLASSIC_ENEMY(element1) ||
5425 IS_CLASSIC_ENEMY(element2)) &&
5426 element1 != EL_DRAGON && element2 != EL_DRAGON &&
5427 element1 != EL_FLAMES && element2 != EL_FLAMES)
5429 ResetGfxAnimation(x, y);
5430 GfxAction[x][y] = ACTION_ATTACKING;
5432 if (IS_PLAYER(x, y))
5433 DrawPlayerField(x, y);
5435 DrawLevelField(x, y);
5437 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5439 MovDelay[x][y] = 50;
5443 RemoveField(newx, newy);
5445 Feld[newx][newy] = EL_FLAMES;
5446 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5449 RemoveField(newx1, newy1);
5451 Feld[newx1][newy1] = EL_FLAMES;
5453 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5456 RemoveField(newx2, newy2);
5458 Feld[newx2][newy2] = EL_FLAMES;
5465 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5466 Feld[newx][newy] == EL_DIAMOND)
5468 if (IS_MOVING(newx, newy))
5469 RemoveMovingField(newx, newy);
5472 Feld[newx][newy] = EL_EMPTY;
5473 DrawLevelField(newx, newy);
5476 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5478 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5479 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5481 if (AmoebaNr[newx][newy])
5483 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5484 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5485 Feld[newx][newy] == EL_BD_AMOEBA)
5486 AmoebaCnt[AmoebaNr[newx][newy]]--;
5491 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
5493 RemoveMovingField(newx, newy);
5496 if (IS_MOVING(newx, newy))
5498 RemoveMovingField(newx, newy);
5503 Feld[newx][newy] = EL_EMPTY;
5504 DrawLevelField(newx, newy);
5507 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5509 else if ((element == EL_PACMAN || element == EL_MOLE)
5510 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5512 if (AmoebaNr[newx][newy])
5514 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5515 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5516 Feld[newx][newy] == EL_BD_AMOEBA)
5517 AmoebaCnt[AmoebaNr[newx][newy]]--;
5520 if (element == EL_MOLE)
5522 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
5523 PlayLevelSound(x, y, SND_MOLE_DIGGING);
5525 ResetGfxAnimation(x, y);
5526 GfxAction[x][y] = ACTION_DIGGING;
5527 DrawLevelField(x, y);
5529 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
5531 return; /* wait for shrinking amoeba */
5533 else /* element == EL_PACMAN */
5535 Feld[newx][newy] = EL_EMPTY;
5536 DrawLevelField(newx, newy);
5537 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
5540 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
5541 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
5542 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
5544 /* wait for shrinking amoeba to completely disappear */
5547 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5549 /* object was running against a wall */
5554 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
5555 if (move_pattern & MV_ANY_DIRECTION &&
5556 move_pattern == MovDir[x][y])
5558 int blocking_element =
5559 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
5561 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
5564 element = Feld[x][y]; /* element might have changed */
5568 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
5569 DrawLevelElementAnimation(x, y, element);
5571 if (DONT_TOUCH(element))
5572 TestIfBadThingTouchesPlayer(x, y);
5577 InitMovingField(x, y, MovDir[x][y]);
5579 PlayLevelSoundAction(x, y, ACTION_MOVING);
5583 ContinueMoving(x, y);
5586 void ContinueMoving(int x, int y)
5588 int element = Feld[x][y];
5589 int stored = Store[x][y];
5590 struct ElementInfo *ei = &element_info[element];
5591 int direction = MovDir[x][y];
5592 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5593 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5594 int newx = x + dx, newy = y + dy;
5595 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
5596 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
5597 boolean last_line = (newy == lev_fieldy - 1);
5599 MovPos[x][y] += getElementMoveStepsize(x, y);
5601 if (pushed_by_player) /* special case: moving object pushed by player */
5602 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5604 if (ABS(MovPos[x][y]) < TILEX)
5606 DrawLevelField(x, y);
5608 return; /* element is still moving */
5611 /* element reached destination field */
5613 Feld[x][y] = EL_EMPTY;
5614 Feld[newx][newy] = element;
5615 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
5617 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
5619 element = Feld[newx][newy] = EL_ACID;
5621 else if (element == EL_MOLE)
5623 Feld[x][y] = EL_SAND;
5625 DrawLevelFieldCrumbledSandNeighbours(x, y);
5627 else if (element == EL_QUICKSAND_FILLING)
5629 element = Feld[newx][newy] = get_next_element(element);
5630 Store[newx][newy] = Store[x][y];
5632 else if (element == EL_QUICKSAND_EMPTYING)
5634 Feld[x][y] = get_next_element(element);
5635 element = Feld[newx][newy] = Store[x][y];
5637 else if (element == EL_MAGIC_WALL_FILLING)
5639 element = Feld[newx][newy] = get_next_element(element);
5640 if (!game.magic_wall_active)
5641 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
5642 Store[newx][newy] = Store[x][y];
5644 else if (element == EL_MAGIC_WALL_EMPTYING)
5646 Feld[x][y] = get_next_element(element);
5647 if (!game.magic_wall_active)
5648 Feld[x][y] = EL_MAGIC_WALL_DEAD;
5649 element = Feld[newx][newy] = Store[x][y];
5651 #if USE_NEW_CUSTOM_VALUE
5652 InitField(newx, newy, FALSE);
5655 else if (element == EL_BD_MAGIC_WALL_FILLING)
5657 element = Feld[newx][newy] = get_next_element(element);
5658 if (!game.magic_wall_active)
5659 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
5660 Store[newx][newy] = Store[x][y];
5662 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
5664 Feld[x][y] = get_next_element(element);
5665 if (!game.magic_wall_active)
5666 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5667 element = Feld[newx][newy] = Store[x][y];
5669 #if USE_NEW_CUSTOM_VALUE
5670 InitField(newx, newy, FALSE);
5673 else if (element == EL_AMOEBA_DROPPING)
5675 Feld[x][y] = get_next_element(element);
5676 element = Feld[newx][newy] = Store[x][y];
5678 else if (element == EL_SOKOBAN_OBJECT)
5681 Feld[x][y] = Back[x][y];
5683 if (Back[newx][newy])
5684 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
5686 Back[x][y] = Back[newx][newy] = 0;
5689 Store[x][y] = EL_EMPTY;
5694 MovDelay[newx][newy] = 0;
5696 if (CAN_CHANGE(element))
5698 /* copy element change control values to new field */
5699 ChangeDelay[newx][newy] = ChangeDelay[x][y];
5700 ChangePage[newx][newy] = ChangePage[x][y];
5701 Changed[newx][newy] = Changed[x][y];
5702 ChangeEvent[newx][newy] = ChangeEvent[x][y];
5704 #if USE_NEW_CUSTOM_VALUE
5705 CustomValue[newx][newy] = CustomValue[x][y];
5709 ChangeDelay[x][y] = 0;
5710 ChangePage[x][y] = -1;
5712 ChangeEvent[x][y] = -1;
5714 #if USE_NEW_CUSTOM_VALUE
5715 CustomValue[x][y] = 0;
5718 /* copy animation control values to new field */
5719 GfxFrame[newx][newy] = GfxFrame[x][y];
5720 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
5721 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
5722 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
5724 Pushed[x][y] = Pushed[newx][newy] = FALSE;
5726 /* some elements can leave other elements behind after moving */
5727 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
5728 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
5729 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
5731 int move_leave_element = ei->move_leave_element;
5733 /* this makes it possible to leave the removed element again */
5734 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
5735 ei->move_leave_element == EL_TRIGGER_ELEMENT)
5736 move_leave_element = stored;
5738 Feld[x][y] = move_leave_element;
5740 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
5741 MovDir[x][y] = direction;
5743 InitField(x, y, FALSE);
5745 if (GFX_CRUMBLED(Feld[x][y]))
5746 DrawLevelFieldCrumbledSandNeighbours(x, y);
5748 if (ELEM_IS_PLAYER(move_leave_element))
5749 RelocatePlayer(x, y, move_leave_element);
5752 /* do this after checking for left-behind element */
5753 ResetGfxAnimation(x, y); /* reset animation values for old field */
5755 if (!CAN_MOVE(element) ||
5756 (CAN_FALL(element) && direction == MV_DOWN &&
5757 (element == EL_SPRING ||
5758 element_info[element].move_pattern == MV_WHEN_PUSHED ||
5759 element_info[element].move_pattern == MV_WHEN_DROPPED)))
5760 GfxDir[x][y] = MovDir[newx][newy] = 0;
5762 DrawLevelField(x, y);
5763 DrawLevelField(newx, newy);
5765 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
5767 /* prevent pushed element from moving on in pushed direction */
5768 if (pushed_by_player && CAN_MOVE(element) &&
5769 element_info[element].move_pattern & MV_ANY_DIRECTION &&
5770 !(element_info[element].move_pattern & direction))
5771 TurnRound(newx, newy);
5773 /* prevent elements on conveyor belt from moving on in last direction */
5774 if (pushed_by_conveyor && CAN_FALL(element) &&
5775 direction & MV_HORIZONTAL)
5776 MovDir[newx][newy] = 0;
5778 if (!pushed_by_player)
5780 int nextx = newx + dx, nexty = newy + dy;
5781 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
5783 WasJustMoving[newx][newy] = 3;
5785 if (CAN_FALL(element) && direction == MV_DOWN)
5786 WasJustFalling[newx][newy] = 3;
5788 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
5789 CheckCollision[newx][newy] = 2;
5792 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
5794 TestIfBadThingTouchesPlayer(newx, newy);
5795 TestIfBadThingTouchesFriend(newx, newy);
5797 if (!IS_CUSTOM_ELEMENT(element))
5798 TestIfBadThingTouchesOtherBadThing(newx, newy);
5800 else if (element == EL_PENGUIN)
5801 TestIfFriendTouchesBadThing(newx, newy);
5803 /* give the player one last chance (one more frame) to move away */
5804 if (CAN_FALL(element) && direction == MV_DOWN &&
5805 (last_line || (!IS_FREE(x, newy + 1) &&
5806 (!IS_PLAYER(x, newy + 1) ||
5807 game.engine_version < VERSION_IDENT(3,1,1,0)))))
5810 if (pushed_by_player && !game.use_change_when_pushing_bug)
5812 int push_side = MV_DIR_OPPOSITE(direction);
5813 struct PlayerInfo *player = PLAYERINFO(x, y);
5815 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
5816 player->index_bit, push_side);
5817 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
5818 player->index_bit, push_side);
5821 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
5823 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
5825 TestIfElementHitsCustomElement(newx, newy, direction);
5826 TestIfPlayerTouchesCustomElement(newx, newy);
5827 TestIfElementTouchesCustomElement(newx, newy);
5830 int AmoebeNachbarNr(int ax, int ay)
5833 int element = Feld[ax][ay];
5835 static int xy[4][2] =
5843 for (i = 0; i < NUM_DIRECTIONS; i++)
5845 int x = ax + xy[i][0];
5846 int y = ay + xy[i][1];
5848 if (!IN_LEV_FIELD(x, y))
5851 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
5852 group_nr = AmoebaNr[x][y];
5858 void AmoebenVereinigen(int ax, int ay)
5860 int i, x, y, xx, yy;
5861 int new_group_nr = AmoebaNr[ax][ay];
5862 static int xy[4][2] =
5870 if (new_group_nr == 0)
5873 for (i = 0; i < NUM_DIRECTIONS; i++)
5878 if (!IN_LEV_FIELD(x, y))
5881 if ((Feld[x][y] == EL_AMOEBA_FULL ||
5882 Feld[x][y] == EL_BD_AMOEBA ||
5883 Feld[x][y] == EL_AMOEBA_DEAD) &&
5884 AmoebaNr[x][y] != new_group_nr)
5886 int old_group_nr = AmoebaNr[x][y];
5888 if (old_group_nr == 0)
5891 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
5892 AmoebaCnt[old_group_nr] = 0;
5893 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
5894 AmoebaCnt2[old_group_nr] = 0;
5896 for (yy = 0; yy < lev_fieldy; yy++)
5898 for (xx = 0; xx < lev_fieldx; xx++)
5900 if (AmoebaNr[xx][yy] == old_group_nr)
5901 AmoebaNr[xx][yy] = new_group_nr;
5908 void AmoebeUmwandeln(int ax, int ay)
5912 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
5914 int group_nr = AmoebaNr[ax][ay];
5919 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
5920 printf("AmoebeUmwandeln(): This should never happen!\n");
5925 for (y = 0; y < lev_fieldy; y++)
5927 for (x = 0; x < lev_fieldx; x++)
5929 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
5932 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
5936 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
5937 SND_AMOEBA_TURNING_TO_GEM :
5938 SND_AMOEBA_TURNING_TO_ROCK));
5943 static int xy[4][2] =
5951 for (i = 0; i < NUM_DIRECTIONS; i++)
5956 if (!IN_LEV_FIELD(x, y))
5959 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
5961 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
5962 SND_AMOEBA_TURNING_TO_GEM :
5963 SND_AMOEBA_TURNING_TO_ROCK));
5970 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
5973 int group_nr = AmoebaNr[ax][ay];
5974 boolean done = FALSE;
5979 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
5980 printf("AmoebeUmwandelnBD(): This should never happen!\n");
5985 for (y = 0; y < lev_fieldy; y++)
5987 for (x = 0; x < lev_fieldx; x++)
5989 if (AmoebaNr[x][y] == group_nr &&
5990 (Feld[x][y] == EL_AMOEBA_DEAD ||
5991 Feld[x][y] == EL_BD_AMOEBA ||
5992 Feld[x][y] == EL_AMOEBA_GROWING))
5995 Feld[x][y] = new_element;
5996 InitField(x, y, FALSE);
5997 DrawLevelField(x, y);
6004 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6005 SND_BD_AMOEBA_TURNING_TO_ROCK :
6006 SND_BD_AMOEBA_TURNING_TO_GEM));
6009 void AmoebeWaechst(int x, int y)
6011 static unsigned long sound_delay = 0;
6012 static unsigned long sound_delay_value = 0;
6014 if (!MovDelay[x][y]) /* start new growing cycle */
6018 if (DelayReached(&sound_delay, sound_delay_value))
6020 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6021 sound_delay_value = 30;
6025 if (MovDelay[x][y]) /* wait some time before growing bigger */
6028 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6030 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6031 6 - MovDelay[x][y]);
6033 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6036 if (!MovDelay[x][y])
6038 Feld[x][y] = Store[x][y];
6040 DrawLevelField(x, y);
6045 void AmoebaDisappearing(int x, int y)
6047 static unsigned long sound_delay = 0;
6048 static unsigned long sound_delay_value = 0;
6050 if (!MovDelay[x][y]) /* start new shrinking cycle */
6054 if (DelayReached(&sound_delay, sound_delay_value))
6055 sound_delay_value = 30;
6058 if (MovDelay[x][y]) /* wait some time before shrinking */
6061 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6063 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6064 6 - MovDelay[x][y]);
6066 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6069 if (!MovDelay[x][y])
6071 Feld[x][y] = EL_EMPTY;
6072 DrawLevelField(x, y);
6074 /* don't let mole enter this field in this cycle;
6075 (give priority to objects falling to this field from above) */
6081 void AmoebeAbleger(int ax, int ay)
6084 int element = Feld[ax][ay];
6085 int graphic = el2img(element);
6086 int newax = ax, neway = ay;
6087 static int xy[4][2] =
6095 if (!level.amoeba_speed)
6097 Feld[ax][ay] = EL_AMOEBA_DEAD;
6098 DrawLevelField(ax, ay);
6102 if (IS_ANIMATED(graphic))
6103 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6105 if (!MovDelay[ax][ay]) /* start making new amoeba field */
6106 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6108 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
6111 if (MovDelay[ax][ay])
6115 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
6118 int x = ax + xy[start][0];
6119 int y = ay + xy[start][1];
6121 if (!IN_LEV_FIELD(x, y))
6124 if (IS_FREE(x, y) ||
6125 CAN_GROW_INTO(Feld[x][y]) ||
6126 Feld[x][y] == EL_QUICKSAND_EMPTY)
6132 if (newax == ax && neway == ay)
6135 else /* normal or "filled" (BD style) amoeba */
6138 boolean waiting_for_player = FALSE;
6140 for (i = 0; i < NUM_DIRECTIONS; i++)
6142 int j = (start + i) % 4;
6143 int x = ax + xy[j][0];
6144 int y = ay + xy[j][1];
6146 if (!IN_LEV_FIELD(x, y))
6149 if (IS_FREE(x, y) ||
6150 CAN_GROW_INTO(Feld[x][y]) ||
6151 Feld[x][y] == EL_QUICKSAND_EMPTY)
6157 else if (IS_PLAYER(x, y))
6158 waiting_for_player = TRUE;
6161 if (newax == ax && neway == ay) /* amoeba cannot grow */
6163 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
6165 Feld[ax][ay] = EL_AMOEBA_DEAD;
6166 DrawLevelField(ax, ay);
6167 AmoebaCnt[AmoebaNr[ax][ay]]--;
6169 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
6171 if (element == EL_AMOEBA_FULL)
6172 AmoebeUmwandeln(ax, ay);
6173 else if (element == EL_BD_AMOEBA)
6174 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6179 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6181 /* amoeba gets larger by growing in some direction */
6183 int new_group_nr = AmoebaNr[ax][ay];
6186 if (new_group_nr == 0)
6188 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6189 printf("AmoebeAbleger(): This should never happen!\n");
6194 AmoebaNr[newax][neway] = new_group_nr;
6195 AmoebaCnt[new_group_nr]++;
6196 AmoebaCnt2[new_group_nr]++;
6198 /* if amoeba touches other amoeba(s) after growing, unify them */
6199 AmoebenVereinigen(newax, neway);
6201 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6203 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6209 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
6210 (neway == lev_fieldy - 1 && newax != ax))
6212 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
6213 Store[newax][neway] = element;
6215 else if (neway == ay)
6217 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
6219 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6223 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
6224 Feld[ax][ay] = EL_AMOEBA_DROPPING;
6225 Store[ax][ay] = EL_AMOEBA_DROP;
6226 ContinueMoving(ax, ay);
6230 DrawLevelField(newax, neway);
6233 void Life(int ax, int ay)
6237 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
6240 int element = Feld[ax][ay];
6241 int graphic = el2img(element);
6242 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
6244 boolean changed = FALSE;
6246 if (IS_ANIMATED(graphic))
6247 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6252 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
6253 MovDelay[ax][ay] = life_time;
6255 if (MovDelay[ax][ay]) /* wait some time before next cycle */
6258 if (MovDelay[ax][ay])
6262 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
6264 int xx = ax+x1, yy = ay+y1;
6267 if (!IN_LEV_FIELD(xx, yy))
6270 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
6272 int x = xx+x2, y = yy+y2;
6274 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
6277 if (((Feld[x][y] == element ||
6278 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
6280 (IS_FREE(x, y) && Stop[x][y]))
6284 if (xx == ax && yy == ay) /* field in the middle */
6286 if (nachbarn < life_parameter[0] ||
6287 nachbarn > life_parameter[1])
6289 Feld[xx][yy] = EL_EMPTY;
6291 DrawLevelField(xx, yy);
6292 Stop[xx][yy] = TRUE;
6296 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
6297 { /* free border field */
6298 if (nachbarn >= life_parameter[2] &&
6299 nachbarn <= life_parameter[3])
6301 Feld[xx][yy] = element;
6302 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6304 DrawLevelField(xx, yy);
6305 Stop[xx][yy] = TRUE;
6312 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
6313 SND_GAME_OF_LIFE_GROWING);
6316 static void InitRobotWheel(int x, int y)
6318 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6321 static void RunRobotWheel(int x, int y)
6323 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
6326 static void StopRobotWheel(int x, int y)
6328 if (ZX == x && ZY == y)
6332 static void InitTimegateWheel(int x, int y)
6334 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
6337 static void RunTimegateWheel(int x, int y)
6339 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
6342 void CheckExit(int x, int y)
6344 if (local_player->gems_still_needed > 0 ||
6345 local_player->sokobanfields_still_needed > 0 ||
6346 local_player->lights_still_needed > 0)
6348 int element = Feld[x][y];
6349 int graphic = el2img(element);
6351 if (IS_ANIMATED(graphic))
6352 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6357 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6360 Feld[x][y] = EL_EXIT_OPENING;
6362 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
6365 void CheckExitSP(int x, int y)
6367 if (local_player->gems_still_needed > 0)
6369 int element = Feld[x][y];
6370 int graphic = el2img(element);
6372 if (IS_ANIMATED(graphic))
6373 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6378 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6381 Feld[x][y] = EL_SP_EXIT_OPENING;
6383 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
6386 static void CloseAllOpenTimegates()
6390 for (y = 0; y < lev_fieldy; y++)
6392 for (x = 0; x < lev_fieldx; x++)
6394 int element = Feld[x][y];
6396 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
6398 Feld[x][y] = EL_TIMEGATE_CLOSING;
6400 PlayLevelSoundAction(x, y, ACTION_CLOSING);
6406 void EdelsteinFunkeln(int x, int y)
6408 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
6411 if (Feld[x][y] == EL_BD_DIAMOND)
6414 if (MovDelay[x][y] == 0) /* next animation frame */
6415 MovDelay[x][y] = 11 * !SimpleRND(500);
6417 if (MovDelay[x][y] != 0) /* wait some time before next frame */
6421 if (setup.direct_draw && MovDelay[x][y])
6422 SetDrawtoField(DRAW_BUFFERED);
6424 DrawLevelElementAnimation(x, y, Feld[x][y]);
6426 if (MovDelay[x][y] != 0)
6428 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
6429 10 - MovDelay[x][y]);
6431 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
6433 if (setup.direct_draw)
6437 dest_x = FX + SCREENX(x) * TILEX;
6438 dest_y = FY + SCREENY(y) * TILEY;
6440 BlitBitmap(drawto_field, window,
6441 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
6442 SetDrawtoField(DRAW_DIRECT);
6448 void MauerWaechst(int x, int y)
6452 if (!MovDelay[x][y]) /* next animation frame */
6453 MovDelay[x][y] = 3 * delay;
6455 if (MovDelay[x][y]) /* wait some time before next frame */
6459 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6461 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
6462 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
6464 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6467 if (!MovDelay[x][y])
6469 if (MovDir[x][y] == MV_LEFT)
6471 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
6472 DrawLevelField(x - 1, y);
6474 else if (MovDir[x][y] == MV_RIGHT)
6476 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
6477 DrawLevelField(x + 1, y);
6479 else if (MovDir[x][y] == MV_UP)
6481 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
6482 DrawLevelField(x, y - 1);
6486 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
6487 DrawLevelField(x, y + 1);
6490 Feld[x][y] = Store[x][y];
6492 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6493 DrawLevelField(x, y);
6498 void MauerAbleger(int ax, int ay)
6500 int element = Feld[ax][ay];
6501 int graphic = el2img(element);
6502 boolean oben_frei = FALSE, unten_frei = FALSE;
6503 boolean links_frei = FALSE, rechts_frei = FALSE;
6504 boolean oben_massiv = FALSE, unten_massiv = FALSE;
6505 boolean links_massiv = FALSE, rechts_massiv = FALSE;
6506 boolean new_wall = FALSE;
6508 if (IS_ANIMATED(graphic))
6509 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6511 if (!MovDelay[ax][ay]) /* start building new wall */
6512 MovDelay[ax][ay] = 6;
6514 if (MovDelay[ax][ay]) /* wait some time before building new wall */
6517 if (MovDelay[ax][ay])
6521 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
6523 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
6525 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
6527 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
6530 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
6531 element == EL_EXPANDABLE_WALL_ANY)
6535 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
6536 Store[ax][ay-1] = element;
6537 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
6538 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
6539 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
6540 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
6545 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
6546 Store[ax][ay+1] = element;
6547 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
6548 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
6549 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
6550 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
6555 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6556 element == EL_EXPANDABLE_WALL_ANY ||
6557 element == EL_EXPANDABLE_WALL)
6561 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
6562 Store[ax-1][ay] = element;
6563 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
6564 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
6565 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
6566 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
6572 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
6573 Store[ax+1][ay] = element;
6574 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
6575 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
6576 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
6577 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
6582 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
6583 DrawLevelField(ax, ay);
6585 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
6587 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
6588 unten_massiv = TRUE;
6589 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
6590 links_massiv = TRUE;
6591 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
6592 rechts_massiv = TRUE;
6594 if (((oben_massiv && unten_massiv) ||
6595 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6596 element == EL_EXPANDABLE_WALL) &&
6597 ((links_massiv && rechts_massiv) ||
6598 element == EL_EXPANDABLE_WALL_VERTICAL))
6599 Feld[ax][ay] = EL_WALL;
6602 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
6605 void CheckForDragon(int x, int y)
6608 boolean dragon_found = FALSE;
6609 static int xy[4][2] =
6617 for (i = 0; i < NUM_DIRECTIONS; i++)
6619 for (j = 0; j < 4; j++)
6621 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
6623 if (IN_LEV_FIELD(xx, yy) &&
6624 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
6626 if (Feld[xx][yy] == EL_DRAGON)
6627 dragon_found = TRUE;
6636 for (i = 0; i < NUM_DIRECTIONS; i++)
6638 for (j = 0; j < 3; j++)
6640 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
6642 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
6644 Feld[xx][yy] = EL_EMPTY;
6645 DrawLevelField(xx, yy);
6654 static void InitBuggyBase(int x, int y)
6656 int element = Feld[x][y];
6657 int activating_delay = FRAMES_PER_SECOND / 4;
6660 (element == EL_SP_BUGGY_BASE ?
6661 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
6662 element == EL_SP_BUGGY_BASE_ACTIVATING ?
6664 element == EL_SP_BUGGY_BASE_ACTIVE ?
6665 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
6668 static void WarnBuggyBase(int x, int y)
6671 static int xy[4][2] =
6679 for (i = 0; i < NUM_DIRECTIONS; i++)
6681 int xx = x + xy[i][0], yy = y + xy[i][1];
6683 if (IS_PLAYER(xx, yy))
6685 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
6692 static void InitTrap(int x, int y)
6694 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
6697 static void ActivateTrap(int x, int y)
6699 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
6702 static void ChangeActiveTrap(int x, int y)
6704 int graphic = IMG_TRAP_ACTIVE;
6706 /* if new animation frame was drawn, correct crumbled sand border */
6707 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
6708 DrawLevelFieldCrumbledSand(x, y);
6711 static int getSpecialActionElement(int element, int number, int base_element)
6713 return (element != EL_EMPTY ? element :
6714 number != -1 ? base_element + number - 1 :
6718 static int getModifiedActionNumber(int value_old, int operator, int operand,
6719 int value_min, int value_max)
6721 int value_new = (operator == CA_MODE_SET ? operand :
6722 operator == CA_MODE_ADD ? value_old + operand :
6723 operator == CA_MODE_SUBTRACT ? value_old - operand :
6724 operator == CA_MODE_MULTIPLY ? value_old * operand :
6725 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
6726 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
6729 return (value_new < value_min ? value_min :
6730 value_new > value_max ? value_max :
6734 static void ExecuteCustomElementAction(int x, int y, int element, int page)
6736 struct ElementInfo *ei = &element_info[element];
6737 struct ElementChangeInfo *change = &ei->change_page[page];
6738 int action_type = change->action_type;
6739 int action_mode = change->action_mode;
6740 int action_arg = change->action_arg;
6743 if (!change->has_action)
6746 /* ---------- determine action paramater values -------------------------- */
6748 int level_time_value =
6749 (level.time > 0 ? TimeLeft :
6752 int action_arg_element =
6753 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
6754 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
6755 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
6758 int action_arg_direction =
6759 (action_arg >= CA_ARG_DIRECTION_LEFT &&
6760 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
6761 action_arg == CA_ARG_DIRECTION_TRIGGER ?
6762 change->actual_trigger_side :
6763 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
6764 MV_DIR_OPPOSITE(change->actual_trigger_side) :
6767 int action_arg_number_min =
6768 (action_type == CA_SET_PLAYER_SPEED ? MOVE_STEPSIZE_MIN :
6771 int action_arg_number_max =
6772 (action_type == CA_SET_PLAYER_SPEED ? MOVE_STEPSIZE_MAX :
6773 action_type == CA_SET_LEVEL_GEMS ? 999 :
6774 action_type == CA_SET_LEVEL_TIME ? 9999 :
6775 action_type == CA_SET_LEVEL_SCORE ? 99999 :
6776 action_type == CA_SET_CE_SCORE ? 9999 :
6777 action_type == CA_SET_CE_VALUE ? 9999 :
6780 int action_arg_number_reset =
6781 (action_type == CA_SET_PLAYER_SPEED ? TILEX/game.initial_move_delay_value :
6782 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
6783 action_type == CA_SET_LEVEL_TIME ? level.time :
6784 action_type == CA_SET_LEVEL_SCORE ? 0 :
6785 action_type == CA_SET_CE_SCORE ? 0 :
6787 action_type == CA_SET_CE_VALUE ? GET_NEW_CUSTOM_VALUE(element) :
6789 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
6793 int action_arg_number =
6794 (action_arg <= CA_ARG_MAX ? action_arg :
6795 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
6796 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
6797 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
6798 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
6799 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
6800 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
6801 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
6802 #if USE_NEW_CUSTOM_VALUE
6803 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
6805 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
6807 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CHANGE_DELAY(change) :
6808 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
6809 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
6810 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
6811 action_arg == CA_ARG_ELEMENT_TARGET ? GET_NEW_CUSTOM_VALUE(change->target_element) :
6812 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_ce_value :
6815 int action_arg_number_old =
6816 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
6817 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
6818 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
6819 action_type == CA_SET_CE_SCORE ? ei->collect_score :
6820 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
6823 int action_arg_number_new =
6824 getModifiedActionNumber(action_arg_number_old,
6825 action_mode, action_arg_number,
6826 action_arg_number_min, action_arg_number_max);
6828 int trigger_player_bits =
6829 (change->actual_trigger_player >= EL_PLAYER_1 &&
6830 change->actual_trigger_player <= EL_PLAYER_4 ?
6831 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
6834 int action_arg_player_bits =
6835 (action_arg >= CA_ARG_PLAYER_1 &&
6836 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
6837 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
6840 /* ---------- execute action -------------------------------------------- */
6849 /* ---------- level actions ------------------------------------------- */
6851 case CA_RESTART_LEVEL:
6853 game.restart_level = TRUE;
6858 case CA_SHOW_ENVELOPE:
6860 int element = getSpecialActionElement(action_arg_element,
6861 action_arg_number, EL_ENVELOPE_1);
6863 if (IS_ENVELOPE(element))
6864 local_player->show_envelope = element;
6869 case CA_SET_LEVEL_TIME:
6871 if (level.time > 0) /* only modify limited time value */
6873 TimeLeft = action_arg_number_new;
6875 DrawGameValue_Time(TimeLeft);
6877 if (!TimeLeft && setup.time_limit)
6878 for (i = 0; i < MAX_PLAYERS; i++)
6879 KillPlayer(&stored_player[i]);
6885 case CA_SET_LEVEL_SCORE:
6887 local_player->score = action_arg_number_new;
6889 DrawGameValue_Score(local_player->score);
6894 case CA_SET_LEVEL_GEMS:
6896 local_player->gems_still_needed = action_arg_number_new;
6898 DrawGameValue_Emeralds(local_player->gems_still_needed);
6903 case CA_SET_LEVEL_GRAVITY:
6905 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
6906 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
6907 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
6912 case CA_SET_LEVEL_WIND:
6914 game.wind_direction = action_arg_direction;
6919 /* ---------- player actions ------------------------------------------ */
6921 case CA_MOVE_PLAYER:
6923 /* automatically move to the next field in specified direction */
6924 for (i = 0; i < MAX_PLAYERS; i++)
6925 if (trigger_player_bits & (1 << i))
6926 stored_player[i].programmed_action = action_arg_direction;
6931 case CA_EXIT_PLAYER:
6933 for (i = 0; i < MAX_PLAYERS; i++)
6934 if (action_arg_player_bits & (1 << i))
6935 stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
6940 case CA_KILL_PLAYER:
6942 for (i = 0; i < MAX_PLAYERS; i++)
6943 if (action_arg_player_bits & (1 << i))
6944 KillPlayer(&stored_player[i]);
6949 case CA_SET_PLAYER_KEYS:
6951 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
6952 int element = getSpecialActionElement(action_arg_element,
6953 action_arg_number, EL_KEY_1);
6955 if (IS_KEY(element))
6957 for (i = 0; i < MAX_PLAYERS; i++)
6959 if (trigger_player_bits & (1 << i))
6961 stored_player[i].key[KEY_NR(element)] = key_state;
6963 DrawGameValue_Keys(stored_player[i].key);
6965 redraw_mask |= REDRAW_DOOR_1;
6973 case CA_SET_PLAYER_SPEED:
6975 for (i = 0; i < MAX_PLAYERS; i++)
6977 if (trigger_player_bits & (1 << i))
6979 int move_stepsize = TILEX / stored_player[i].move_delay_value;
6981 if (action_arg == CA_ARG_SPEED_SLOWER ||
6982 action_arg == CA_ARG_SPEED_FASTER)
6984 action_arg_number = 2;
6985 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
6990 getModifiedActionNumber(move_stepsize,
6993 action_arg_number_min,
6994 action_arg_number_max);
6996 /* make sure that value is power of 2 */
6997 move_stepsize = (1 << log_2(move_stepsize));
6999 /* do no immediately change -- the player might just be moving */
7000 stored_player[i].move_delay_value_next = TILEX / move_stepsize;
7002 stored_player[i].cannot_move =
7003 (action_arg == CA_ARG_SPEED_NOT_MOVING ? TRUE : FALSE);
7010 case CA_SET_PLAYER_SHIELD:
7012 for (i = 0; i < MAX_PLAYERS; i++)
7014 if (trigger_player_bits & (1 << i))
7016 if (action_arg == CA_ARG_SHIELD_OFF)
7018 stored_player[i].shield_normal_time_left = 0;
7019 stored_player[i].shield_deadly_time_left = 0;
7021 else if (action_arg == CA_ARG_SHIELD_NORMAL)
7023 stored_player[i].shield_normal_time_left = 999999;
7025 else if (action_arg == CA_ARG_SHIELD_DEADLY)
7027 stored_player[i].shield_normal_time_left = 999999;
7028 stored_player[i].shield_deadly_time_left = 999999;
7036 case CA_SET_PLAYER_ARTWORK:
7038 for (i = 0; i < MAX_PLAYERS; i++)
7040 if (trigger_player_bits & (1 << i))
7042 int artwork_element = action_arg_element;
7044 if (action_arg == CA_ARG_ELEMENT_RESET)
7045 artwork_element = stored_player[i].element_nr;
7047 stored_player[i].artwork_element = artwork_element;
7049 SetPlayerWaiting(&stored_player[i], FALSE);
7051 /* set number of special actions for bored and sleeping animation */
7052 stored_player[i].num_special_action_bored =
7053 get_num_special_action(artwork_element,
7054 ACTION_BORING_1, ACTION_BORING_LAST);
7055 stored_player[i].num_special_action_sleeping =
7056 get_num_special_action(artwork_element,
7057 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
7064 /* ---------- CE actions ---------------------------------------------- */
7066 case CA_SET_CE_SCORE:
7068 ei->collect_score = action_arg_number_new;
7073 case CA_SET_CE_VALUE:
7075 #if USE_NEW_CUSTOM_VALUE
7076 int last_custom_value = CustomValue[x][y];
7078 CustomValue[x][y] = action_arg_number_new;
7081 printf("::: Count == %d\n", CustomValue[x][y]);
7084 if (CustomValue[x][y] == 0 && last_custom_value > 0)
7087 printf("::: CE_VALUE_GETS_ZERO\n");
7090 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
7091 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
7103 static void ChangeElementNowExt(struct ElementChangeInfo *change,
7104 int x, int y, int target_element)
7106 int previous_move_direction = MovDir[x][y];
7107 #if USE_NEW_CUSTOM_VALUE
7108 int last_ce_value = CustomValue[x][y];
7110 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7111 IS_WALKABLE(Feld[x][y]));
7113 /* check if element under player changes from accessible to unaccessible
7114 (needed for special case of dropping element which then changes) */
7115 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7116 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
7124 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
7125 RemoveMovingField(x, y);
7129 Feld[x][y] = target_element;
7131 ResetGfxAnimation(x, y);
7132 ResetRandomAnimationValue(x, y);
7134 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7135 MovDir[x][y] = previous_move_direction;
7137 #if USE_NEW_CUSTOM_VALUE
7138 if (element_info[Feld[x][y]].use_last_ce_value)
7139 CustomValue[x][y] = last_ce_value;
7142 InitField_WithBug1(x, y, FALSE);
7144 DrawLevelField(x, y);
7146 if (GFX_CRUMBLED(Feld[x][y]))
7147 DrawLevelFieldCrumbledSandNeighbours(x, y);
7150 /* "Changed[][]" not set yet to allow "entered by player" change one time */
7151 if (ELEM_IS_PLAYER(target_element))
7152 RelocatePlayer(x, y, target_element);
7154 Changed[x][y]++; /* count number of changes in the same frame */
7156 TestIfBadThingTouchesPlayer(x, y);
7157 TestIfPlayerTouchesCustomElement(x, y);
7158 TestIfElementTouchesCustomElement(x, y);
7161 static boolean ChangeElementNow(int x, int y, int element, int page)
7163 struct ElementChangeInfo *change = &element_info[element].change_page[page];
7165 int old_element = Feld[x][y];
7167 /* always use default change event to prevent running into a loop */
7168 if (ChangeEvent[x][y] == -1)
7169 ChangeEvent[x][y] = CE_DELAY;
7171 if (ChangeEvent[x][y] == CE_DELAY)
7173 /* reset actual trigger element, trigger player and action element */
7174 change->actual_trigger_element = EL_EMPTY;
7175 change->actual_trigger_player = EL_PLAYER_1;
7176 change->actual_trigger_side = CH_SIDE_NONE;
7177 change->actual_trigger_ce_value = 0;
7180 /* do not change elements more than a specified maximum number of changes */
7181 if (Changed[x][y] >= game.max_num_changes_per_frame)
7184 Changed[x][y]++; /* count number of changes in the same frame */
7186 if (change->explode)
7193 if (change->use_target_content)
7195 boolean complete_replace = TRUE;
7196 boolean can_replace[3][3];
7199 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7202 boolean is_walkable;
7203 boolean is_diggable;
7204 boolean is_collectible;
7205 boolean is_removable;
7206 boolean is_destructible;
7207 int ex = x + xx - 1;
7208 int ey = y + yy - 1;
7209 int content_element = change->target_content.e[xx][yy];
7212 can_replace[xx][yy] = TRUE;
7214 if (ex == x && ey == y) /* do not check changing element itself */
7217 if (content_element == EL_EMPTY_SPACE)
7219 can_replace[xx][yy] = FALSE; /* do not replace border with space */
7224 if (!IN_LEV_FIELD(ex, ey))
7226 can_replace[xx][yy] = FALSE;
7227 complete_replace = FALSE;
7234 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7235 e = MovingOrBlocked2Element(ex, ey);
7237 is_empty = (IS_FREE(ex, ey) ||
7238 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7240 is_walkable = (is_empty || IS_WALKABLE(e));
7241 is_diggable = (is_empty || IS_DIGGABLE(e));
7242 is_collectible = (is_empty || IS_COLLECTIBLE(e));
7243 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
7244 is_removable = (is_diggable || is_collectible);
7246 can_replace[xx][yy] =
7247 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
7248 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
7249 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
7250 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
7251 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
7252 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
7253 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
7255 if (!can_replace[xx][yy])
7256 complete_replace = FALSE;
7259 if (!change->only_if_complete || complete_replace)
7261 boolean something_has_changed = FALSE;
7263 if (change->only_if_complete && change->use_random_replace &&
7264 RND(100) < change->random_percentage)
7267 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7269 int ex = x + xx - 1;
7270 int ey = y + yy - 1;
7271 int content_element;
7273 if (can_replace[xx][yy] && (!change->use_random_replace ||
7274 RND(100) < change->random_percentage))
7276 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7277 RemoveMovingField(ex, ey);
7279 ChangeEvent[ex][ey] = ChangeEvent[x][y];
7281 content_element = change->target_content.e[xx][yy];
7282 target_element = GET_TARGET_ELEMENT(content_element, change);
7284 ChangeElementNowExt(change, ex, ey, target_element);
7286 something_has_changed = TRUE;
7288 /* for symmetry reasons, freeze newly created border elements */
7289 if (ex != x || ey != y)
7290 Stop[ex][ey] = TRUE; /* no more moving in this frame */
7294 if (something_has_changed)
7296 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7297 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
7303 target_element = GET_TARGET_ELEMENT(change->target_element, change);
7305 ChangeElementNowExt(change, x, y, target_element);
7307 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7308 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
7311 /* this uses direct change before indirect change */
7312 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
7317 #if USE_NEW_DELAYED_ACTION
7319 static void ChangeElement(int x, int y, int page)
7321 int element = MovingOrBlocked2Element(x, y);
7322 struct ElementInfo *ei = &element_info[element];
7323 struct ElementChangeInfo *change = &ei->change_page[page];
7326 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
7327 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
7330 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
7331 x, y, element, element_info[element].token_name);
7332 printf("ChangeElement(): This should never happen!\n");
7337 /* this can happen with classic bombs on walkable, changing elements */
7338 if (!CAN_CHANGE_OR_HAS_ACTION(element))
7341 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
7342 ChangeDelay[x][y] = 0;
7348 if (ChangeDelay[x][y] == 0) /* initialize element change */
7350 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
7352 if (change->can_change)
7354 ResetGfxAnimation(x, y);
7355 ResetRandomAnimationValue(x, y);
7357 if (change->pre_change_function)
7358 change->pre_change_function(x, y);
7362 ChangeDelay[x][y]--;
7364 if (ChangeDelay[x][y] != 0) /* continue element change */
7366 if (change->can_change)
7368 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7370 if (IS_ANIMATED(graphic))
7371 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7373 if (change->change_function)
7374 change->change_function(x, y);
7377 else /* finish element change */
7379 if (ChangePage[x][y] != -1) /* remember page from delayed change */
7381 page = ChangePage[x][y];
7382 ChangePage[x][y] = -1;
7384 change = &ei->change_page[page];
7387 if (IS_MOVING(x, y)) /* never change a running system ;-) */
7389 ChangeDelay[x][y] = 1; /* try change after next move step */
7390 ChangePage[x][y] = page; /* remember page to use for change */
7395 if (change->can_change)
7397 if (ChangeElementNow(x, y, element, page))
7399 if (change->post_change_function)
7400 change->post_change_function(x, y);
7404 if (change->has_action)
7405 ExecuteCustomElementAction(x, y, element, page);
7411 static void ChangeElement(int x, int y, int page)
7413 int element = MovingOrBlocked2Element(x, y);
7414 struct ElementInfo *ei = &element_info[element];
7415 struct ElementChangeInfo *change = &ei->change_page[page];
7418 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
7421 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
7422 x, y, element, element_info[element].token_name);
7423 printf("ChangeElement(): This should never happen!\n");
7428 /* this can happen with classic bombs on walkable, changing elements */
7429 if (!CAN_CHANGE(element))
7432 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
7433 ChangeDelay[x][y] = 0;
7439 if (ChangeDelay[x][y] == 0) /* initialize element change */
7441 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
7443 ResetGfxAnimation(x, y);
7444 ResetRandomAnimationValue(x, y);
7446 if (change->pre_change_function)
7447 change->pre_change_function(x, y);
7450 ChangeDelay[x][y]--;
7452 if (ChangeDelay[x][y] != 0) /* continue element change */
7454 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7456 if (IS_ANIMATED(graphic))
7457 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7459 if (change->change_function)
7460 change->change_function(x, y);
7462 else /* finish element change */
7464 if (ChangePage[x][y] != -1) /* remember page from delayed change */
7466 page = ChangePage[x][y];
7467 ChangePage[x][y] = -1;
7469 change = &ei->change_page[page];
7472 if (IS_MOVING(x, y)) /* never change a running system ;-) */
7474 ChangeDelay[x][y] = 1; /* try change after next move step */
7475 ChangePage[x][y] = page; /* remember page to use for change */
7480 if (ChangeElementNow(x, y, element, page))
7482 if (change->post_change_function)
7483 change->post_change_function(x, y);
7490 static boolean CheckTriggeredElementChangeExt(int x, int y,
7491 int trigger_element,
7497 boolean change_done_any = FALSE;
7498 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
7501 if (!(trigger_events[trigger_element][trigger_event]))
7504 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7506 int element = EL_CUSTOM_START + i;
7507 boolean change_done = FALSE;
7510 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
7511 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7514 for (p = 0; p < element_info[element].num_change_pages; p++)
7516 struct ElementChangeInfo *change = &element_info[element].change_page[p];
7518 if (change->can_change_or_has_action &&
7519 change->has_event[trigger_event] &&
7520 change->trigger_side & trigger_side &&
7521 change->trigger_player & trigger_player &&
7522 change->trigger_page & trigger_page_bits &&
7523 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
7525 change->actual_trigger_element = trigger_element;
7526 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7527 change->actual_trigger_side = trigger_side;
7528 change->actual_trigger_ce_value = CustomValue[x][y];
7530 if ((change->can_change && !change_done) || change->has_action)
7534 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7536 if (Feld[x][y] == element)
7538 if (change->can_change && !change_done)
7540 ChangeDelay[x][y] = 1;
7541 ChangeEvent[x][y] = trigger_event;
7542 ChangeElement(x, y, p);
7544 #if USE_NEW_DELAYED_ACTION
7545 else if (change->has_action)
7547 ExecuteCustomElementAction(x, y, element, p);
7548 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7551 if (change->has_action)
7553 ExecuteCustomElementAction(x, y, element, p);
7554 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7560 if (change->can_change)
7563 change_done_any = TRUE;
7570 return change_done_any;
7573 static boolean CheckElementChangeExt(int x, int y,
7575 int trigger_element,
7580 boolean change_done = FALSE;
7583 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
7584 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7587 if (Feld[x][y] == EL_BLOCKED)
7589 Blocked2Moving(x, y, &x, &y);
7590 element = Feld[x][y];
7593 if (Feld[x][y] != element) /* check if element has already changed */
7596 for (p = 0; p < element_info[element].num_change_pages; p++)
7598 struct ElementChangeInfo *change = &element_info[element].change_page[p];
7600 boolean check_trigger_element =
7601 (trigger_event == CE_TOUCHING_X ||
7602 trigger_event == CE_HITTING_X ||
7603 trigger_event == CE_HIT_BY_X);
7605 if (change->can_change_or_has_action &&
7606 change->has_event[trigger_event] &&
7607 change->trigger_side & trigger_side &&
7608 change->trigger_player & trigger_player &&
7609 (!check_trigger_element ||
7610 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
7612 change->actual_trigger_element = trigger_element;
7613 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7614 change->actual_trigger_side = trigger_side;
7615 change->actual_trigger_ce_value = CustomValue[x][y];
7617 if (change->can_change && !change_done)
7619 ChangeDelay[x][y] = 1;
7620 ChangeEvent[x][y] = trigger_event;
7621 ChangeElement(x, y, p);
7625 #if USE_NEW_DELAYED_ACTION
7626 else if (change->has_action)
7628 ExecuteCustomElementAction(x, y, element, p);
7629 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7632 if (change->has_action)
7634 ExecuteCustomElementAction(x, y, element, p);
7635 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7644 static void PlayPlayerSound(struct PlayerInfo *player)
7646 int jx = player->jx, jy = player->jy;
7647 int sound_element = player->artwork_element;
7648 int last_action = player->last_action_waiting;
7649 int action = player->action_waiting;
7651 if (player->is_waiting)
7653 if (action != last_action)
7654 PlayLevelSoundElementAction(jx, jy, sound_element, action);
7656 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
7660 if (action != last_action)
7661 StopSound(element_info[sound_element].sound[last_action]);
7663 if (last_action == ACTION_SLEEPING)
7664 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
7668 static void PlayAllPlayersSound()
7672 for (i = 0; i < MAX_PLAYERS; i++)
7673 if (stored_player[i].active)
7674 PlayPlayerSound(&stored_player[i]);
7677 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
7679 boolean last_waiting = player->is_waiting;
7680 int move_dir = player->MovDir;
7682 player->last_action_waiting = player->action_waiting;
7686 if (!last_waiting) /* not waiting -> waiting */
7688 player->is_waiting = TRUE;
7690 player->frame_counter_bored =
7692 game.player_boring_delay_fixed +
7693 SimpleRND(game.player_boring_delay_random);
7694 player->frame_counter_sleeping =
7696 game.player_sleeping_delay_fixed +
7697 SimpleRND(game.player_sleeping_delay_random);
7699 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
7702 if (game.player_sleeping_delay_fixed +
7703 game.player_sleeping_delay_random > 0 &&
7704 player->anim_delay_counter == 0 &&
7705 player->post_delay_counter == 0 &&
7706 FrameCounter >= player->frame_counter_sleeping)
7707 player->is_sleeping = TRUE;
7708 else if (game.player_boring_delay_fixed +
7709 game.player_boring_delay_random > 0 &&
7710 FrameCounter >= player->frame_counter_bored)
7711 player->is_bored = TRUE;
7713 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
7714 player->is_bored ? ACTION_BORING :
7717 if (player->is_sleeping)
7719 if (player->num_special_action_sleeping > 0)
7721 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7723 int last_special_action = player->special_action_sleeping;
7724 int num_special_action = player->num_special_action_sleeping;
7725 int special_action =
7726 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
7727 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
7728 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
7729 last_special_action + 1 : ACTION_SLEEPING);
7730 int special_graphic =
7731 el_act_dir2img(player->artwork_element, special_action, move_dir);
7733 player->anim_delay_counter =
7734 graphic_info[special_graphic].anim_delay_fixed +
7735 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7736 player->post_delay_counter =
7737 graphic_info[special_graphic].post_delay_fixed +
7738 SimpleRND(graphic_info[special_graphic].post_delay_random);
7740 player->special_action_sleeping = special_action;
7743 if (player->anim_delay_counter > 0)
7745 player->action_waiting = player->special_action_sleeping;
7746 player->anim_delay_counter--;
7748 else if (player->post_delay_counter > 0)
7750 player->post_delay_counter--;
7754 else if (player->is_bored)
7756 if (player->num_special_action_bored > 0)
7758 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7760 int special_action =
7761 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
7762 int special_graphic =
7763 el_act_dir2img(player->artwork_element, special_action, move_dir);
7765 player->anim_delay_counter =
7766 graphic_info[special_graphic].anim_delay_fixed +
7767 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7768 player->post_delay_counter =
7769 graphic_info[special_graphic].post_delay_fixed +
7770 SimpleRND(graphic_info[special_graphic].post_delay_random);
7772 player->special_action_bored = special_action;
7775 if (player->anim_delay_counter > 0)
7777 player->action_waiting = player->special_action_bored;
7778 player->anim_delay_counter--;
7780 else if (player->post_delay_counter > 0)
7782 player->post_delay_counter--;
7787 else if (last_waiting) /* waiting -> not waiting */
7789 player->is_waiting = FALSE;
7790 player->is_bored = FALSE;
7791 player->is_sleeping = FALSE;
7793 player->frame_counter_bored = -1;
7794 player->frame_counter_sleeping = -1;
7796 player->anim_delay_counter = 0;
7797 player->post_delay_counter = 0;
7799 player->action_waiting = ACTION_DEFAULT;
7801 player->special_action_bored = ACTION_DEFAULT;
7802 player->special_action_sleeping = ACTION_DEFAULT;
7806 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
7808 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7809 int left = player_action & JOY_LEFT;
7810 int right = player_action & JOY_RIGHT;
7811 int up = player_action & JOY_UP;
7812 int down = player_action & JOY_DOWN;
7813 int button1 = player_action & JOY_BUTTON_1;
7814 int button2 = player_action & JOY_BUTTON_2;
7815 int dx = (left ? -1 : right ? 1 : 0);
7816 int dy = (up ? -1 : down ? 1 : 0);
7818 if (!player->active || tape.pausing)
7824 snapped = SnapField(player, dx, dy);
7828 dropped = DropElement(player);
7830 moved = MovePlayer(player, dx, dy);
7833 if (tape.single_step && tape.recording && !tape.pausing)
7835 if (button1 || (dropped && !moved))
7837 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7838 SnapField(player, 0, 0); /* stop snapping */
7842 SetPlayerWaiting(player, FALSE);
7844 return player_action;
7848 /* no actions for this player (no input at player's configured device) */
7850 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7851 SnapField(player, 0, 0);
7852 CheckGravityMovementWhenNotMoving(player);
7854 if (player->MovPos == 0)
7855 SetPlayerWaiting(player, TRUE);
7857 if (player->MovPos == 0) /* needed for tape.playing */
7858 player->is_moving = FALSE;
7860 player->is_dropping = FALSE;
7866 void AdvanceFrameAndPlayerCounters(int player_nr)
7870 /* advance frame counters (global frame counter and time frame counter) */
7874 /* advance player counters (counters for move delay, move animation etc.) */
7875 for (i = 0; i < MAX_PLAYERS; i++)
7877 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
7878 int move_delay_value = stored_player[i].move_delay_value;
7879 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
7881 if (!advance_player_counters) /* not all players may be affected */
7884 #if USE_NEW_PLAYER_ANIM
7885 if (move_frames == 0) /* less than one move per game frame */
7887 int stepsize = TILEX / move_delay_value;
7888 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
7889 int count = (stored_player[i].is_moving ?
7890 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
7892 if (count % delay == 0)
7897 stored_player[i].Frame += move_frames;
7899 if (stored_player[i].MovPos != 0)
7900 stored_player[i].StepFrame += move_frames;
7902 if (stored_player[i].move_delay > 0)
7903 stored_player[i].move_delay--;
7905 /* due to bugs in previous versions, counter must count up, not down */
7906 if (stored_player[i].push_delay != -1)
7907 stored_player[i].push_delay++;
7909 if (stored_player[i].drop_delay > 0)
7910 stored_player[i].drop_delay--;
7916 static unsigned long game_frame_delay = 0;
7917 unsigned long game_frame_delay_value;
7918 int magic_wall_x = 0, magic_wall_y = 0;
7919 int i, x, y, element, graphic;
7920 byte *recorded_player_action;
7921 byte summarized_player_action = 0;
7922 byte tape_action[MAX_PLAYERS];
7924 if (game_status != GAME_MODE_PLAYING)
7927 game_frame_delay_value =
7928 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
7930 if (tape.playing && tape.warp_forward && !tape.pausing)
7931 game_frame_delay_value = 0;
7933 /* ---------- main game synchronization point ---------- */
7935 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
7937 if (network_playing && !network_player_action_received)
7939 /* try to get network player actions in time */
7941 #if defined(NETWORK_AVALIABLE)
7942 /* last chance to get network player actions without main loop delay */
7946 /* game was quit by network peer */
7947 if (game_status != GAME_MODE_PLAYING)
7950 if (!network_player_action_received)
7951 return; /* failed to get network player actions in time */
7957 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
7960 /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
7961 if (recorded_player_action == NULL && tape.pausing)
7965 for (i = 0; i < MAX_PLAYERS; i++)
7967 summarized_player_action |= stored_player[i].action;
7969 if (!network_playing)
7970 stored_player[i].effective_action = stored_player[i].action;
7973 #if defined(NETWORK_AVALIABLE)
7974 if (network_playing)
7975 SendToServer_MovePlayer(summarized_player_action);
7978 if (!options.network && !setup.team_mode)
7979 local_player->effective_action = summarized_player_action;
7981 if (recorded_player_action != NULL)
7982 for (i = 0; i < MAX_PLAYERS; i++)
7983 stored_player[i].effective_action = recorded_player_action[i];
7985 for (i = 0; i < MAX_PLAYERS; i++)
7987 tape_action[i] = stored_player[i].effective_action;
7989 if (tape.recording && tape_action[i] && !tape.player_participates[i])
7990 tape.player_participates[i] = TRUE; /* player just appeared from CE */
7993 /* only save actions from input devices, but not programmed actions */
7995 TapeRecordAction(tape_action);
7997 for (i = 0; i < MAX_PLAYERS; i++)
7999 int actual_player_action = stored_player[i].effective_action;
8002 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8003 - rnd_equinox_tetrachloride 048
8004 - rnd_equinox_tetrachloride_ii 096
8005 - rnd_emanuel_schmieg 002
8006 - doctor_sloan_ww 001, 020
8008 if (stored_player[i].MovPos == 0)
8009 CheckGravityMovement(&stored_player[i]);
8012 /* overwrite programmed action with tape action */
8013 if (stored_player[i].programmed_action)
8014 actual_player_action = stored_player[i].programmed_action;
8017 PlayerActions(&stored_player[i], actual_player_action);
8019 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8021 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8022 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8025 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8028 network_player_action_received = FALSE;
8030 ScrollScreen(NULL, SCROLL_GO_ON);
8032 /* for backwards compatibility, the following code emulates a fixed bug that
8033 occured when pushing elements (causing elements that just made their last
8034 pushing step to already (if possible) make their first falling step in the
8035 same game frame, which is bad); this code is also needed to use the famous
8036 "spring push bug" which is used in older levels and might be wanted to be
8037 used also in newer levels, but in this case the buggy pushing code is only
8038 affecting the "spring" element and no other elements */
8040 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8042 for (i = 0; i < MAX_PLAYERS; i++)
8044 struct PlayerInfo *player = &stored_player[i];
8048 if (player->active && player->is_pushing && player->is_moving &&
8050 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8051 Feld[x][y] == EL_SPRING))
8053 ContinueMoving(x, y);
8055 /* continue moving after pushing (this is actually a bug) */
8056 if (!IS_MOVING(x, y))
8064 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8067 ChangeEvent[x][y] = -1;
8069 /* this must be handled before main playfield loop */
8070 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
8073 if (MovDelay[x][y] <= 0)
8077 #if USE_NEW_SNAP_DELAY
8078 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
8081 if (MovDelay[x][y] <= 0)
8084 DrawLevelField(x, y);
8086 TestIfElementTouchesCustomElement(x, y); /* for empty space */
8092 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
8094 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
8095 printf("GameActions(): This should never happen!\n");
8097 ChangePage[x][y] = -1;
8102 if (WasJustMoving[x][y] > 0)
8103 WasJustMoving[x][y]--;
8104 if (WasJustFalling[x][y] > 0)
8105 WasJustFalling[x][y]--;
8106 if (CheckCollision[x][y] > 0)
8107 CheckCollision[x][y]--;
8111 /* reset finished pushing action (not done in ContinueMoving() to allow
8112 continous pushing animation for elements with zero push delay) */
8113 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
8115 ResetGfxAnimation(x, y);
8116 DrawLevelField(x, y);
8120 if (IS_BLOCKED(x, y))
8124 Blocked2Moving(x, y, &oldx, &oldy);
8125 if (!IS_MOVING(oldx, oldy))
8127 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
8128 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
8129 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
8130 printf("GameActions(): This should never happen!\n");
8136 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8138 element = Feld[x][y];
8139 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8141 if (graphic_info[graphic].anim_global_sync)
8142 GfxFrame[x][y] = FrameCounter;
8144 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
8145 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
8146 ResetRandomAnimationValue(x, y);
8148 SetRandomAnimationValue(x, y);
8150 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
8152 if (IS_INACTIVE(element))
8154 if (IS_ANIMATED(graphic))
8155 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8160 /* this may take place after moving, so 'element' may have changed */
8161 if (IS_CHANGING(x, y) &&
8162 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
8164 int page = element_info[element].event_page_nr[CE_DELAY];
8166 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
8170 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
8174 ChangeElement(x, y, page);
8176 if (CAN_CHANGE(element))
8177 ChangeElement(x, y, page);
8179 if (HAS_ACTION(element))
8180 ExecuteCustomElementAction(x, y, element, page);
8185 element = Feld[x][y];
8186 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8189 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
8193 element = Feld[x][y];
8194 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8196 if (IS_ANIMATED(graphic) &&
8199 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8201 if (IS_GEM(element) || element == EL_SP_INFOTRON)
8202 EdelsteinFunkeln(x, y);
8204 else if ((element == EL_ACID ||
8205 element == EL_EXIT_OPEN ||
8206 element == EL_SP_EXIT_OPEN ||
8207 element == EL_SP_TERMINAL ||
8208 element == EL_SP_TERMINAL_ACTIVE ||
8209 element == EL_EXTRA_TIME ||
8210 element == EL_SHIELD_NORMAL ||
8211 element == EL_SHIELD_DEADLY) &&
8212 IS_ANIMATED(graphic))
8213 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8214 else if (IS_MOVING(x, y))
8215 ContinueMoving(x, y);
8216 else if (IS_ACTIVE_BOMB(element))
8217 CheckDynamite(x, y);
8218 else if (element == EL_AMOEBA_GROWING)
8219 AmoebeWaechst(x, y);
8220 else if (element == EL_AMOEBA_SHRINKING)
8221 AmoebaDisappearing(x, y);
8223 #if !USE_NEW_AMOEBA_CODE
8224 else if (IS_AMOEBALIVE(element))
8225 AmoebeAbleger(x, y);
8228 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
8230 else if (element == EL_EXIT_CLOSED)
8232 else if (element == EL_SP_EXIT_CLOSED)
8234 else if (element == EL_EXPANDABLE_WALL_GROWING)
8236 else if (element == EL_EXPANDABLE_WALL ||
8237 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8238 element == EL_EXPANDABLE_WALL_VERTICAL ||
8239 element == EL_EXPANDABLE_WALL_ANY)
8241 else if (element == EL_FLAMES)
8242 CheckForDragon(x, y);
8243 else if (element == EL_EXPLOSION)
8244 ; /* drawing of correct explosion animation is handled separately */
8245 else if (element == EL_ELEMENT_SNAPPING)
8248 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
8250 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8253 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
8254 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8256 if (IS_BELT_ACTIVE(element))
8257 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
8259 if (game.magic_wall_active)
8261 int jx = local_player->jx, jy = local_player->jy;
8263 /* play the element sound at the position nearest to the player */
8264 if ((element == EL_MAGIC_WALL_FULL ||
8265 element == EL_MAGIC_WALL_ACTIVE ||
8266 element == EL_MAGIC_WALL_EMPTYING ||
8267 element == EL_BD_MAGIC_WALL_FULL ||
8268 element == EL_BD_MAGIC_WALL_ACTIVE ||
8269 element == EL_BD_MAGIC_WALL_EMPTYING) &&
8270 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
8278 #if USE_NEW_AMOEBA_CODE
8279 /* new experimental amoeba growth stuff */
8280 if (!(FrameCounter % 8))
8282 static unsigned long random = 1684108901;
8284 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
8286 x = RND(lev_fieldx);
8287 y = RND(lev_fieldy);
8288 element = Feld[x][y];
8290 if (!IS_PLAYER(x,y) &&
8291 (element == EL_EMPTY ||
8292 CAN_GROW_INTO(element) ||
8293 element == EL_QUICKSAND_EMPTY ||
8294 element == EL_ACID_SPLASH_LEFT ||
8295 element == EL_ACID_SPLASH_RIGHT))
8297 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8298 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8299 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8300 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8301 Feld[x][y] = EL_AMOEBA_DROP;
8304 random = random * 129 + 1;
8310 if (game.explosions_delayed)
8313 game.explosions_delayed = FALSE;
8315 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8317 element = Feld[x][y];
8319 if (ExplodeField[x][y])
8320 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
8321 else if (element == EL_EXPLOSION)
8322 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8324 ExplodeField[x][y] = EX_TYPE_NONE;
8327 game.explosions_delayed = TRUE;
8330 if (game.magic_wall_active)
8332 if (!(game.magic_wall_time_left % 4))
8334 int element = Feld[magic_wall_x][magic_wall_y];
8336 if (element == EL_BD_MAGIC_WALL_FULL ||
8337 element == EL_BD_MAGIC_WALL_ACTIVE ||
8338 element == EL_BD_MAGIC_WALL_EMPTYING)
8339 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
8341 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
8344 if (game.magic_wall_time_left > 0)
8346 game.magic_wall_time_left--;
8347 if (!game.magic_wall_time_left)
8349 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8351 element = Feld[x][y];
8353 if (element == EL_MAGIC_WALL_ACTIVE ||
8354 element == EL_MAGIC_WALL_FULL)
8356 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8357 DrawLevelField(x, y);
8359 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
8360 element == EL_BD_MAGIC_WALL_FULL)
8362 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8363 DrawLevelField(x, y);
8367 game.magic_wall_active = FALSE;
8372 if (game.light_time_left > 0)
8374 game.light_time_left--;
8376 if (game.light_time_left == 0)
8377 RedrawAllLightSwitchesAndInvisibleElements();
8380 if (game.timegate_time_left > 0)
8382 game.timegate_time_left--;
8384 if (game.timegate_time_left == 0)
8385 CloseAllOpenTimegates();
8388 if (game.lenses_time_left > 0)
8390 game.lenses_time_left--;
8392 if (game.lenses_time_left == 0)
8393 RedrawAllInvisibleElementsForLenses();
8396 if (game.magnify_time_left > 0)
8398 game.magnify_time_left--;
8400 if (game.magnify_time_left == 0)
8401 RedrawAllInvisibleElementsForMagnifier();
8404 for (i = 0; i < MAX_PLAYERS; i++)
8406 struct PlayerInfo *player = &stored_player[i];
8408 if (SHIELD_ON(player))
8410 if (player->shield_deadly_time_left)
8411 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
8412 else if (player->shield_normal_time_left)
8413 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
8417 if (TimeFrames >= FRAMES_PER_SECOND)
8422 for (i = 0; i < MAX_PLAYERS; i++)
8424 struct PlayerInfo *player = &stored_player[i];
8426 if (SHIELD_ON(player))
8428 player->shield_normal_time_left--;
8430 if (player->shield_deadly_time_left > 0)
8431 player->shield_deadly_time_left--;
8435 if (!level.use_step_counter)
8443 if (TimeLeft <= 10 && setup.time_limit)
8444 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8446 DrawGameValue_Time(TimeLeft);
8448 if (!TimeLeft && setup.time_limit)
8449 for (i = 0; i < MAX_PLAYERS; i++)
8450 KillPlayer(&stored_player[i]);
8452 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8453 DrawGameValue_Time(TimePlayed);
8456 if (tape.recording || tape.playing)
8457 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
8461 PlayAllPlayersSound();
8463 if (options.debug) /* calculate frames per second */
8465 static unsigned long fps_counter = 0;
8466 static int fps_frames = 0;
8467 unsigned long fps_delay_ms = Counter() - fps_counter;
8471 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
8473 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
8476 fps_counter = Counter();
8479 redraw_mask |= REDRAW_FPS;
8482 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
8484 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
8486 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
8488 local_player->show_envelope = 0;
8491 /* use random number generator in every frame to make it less predictable */
8492 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
8496 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
8498 int min_x = x, min_y = y, max_x = x, max_y = y;
8501 for (i = 0; i < MAX_PLAYERS; i++)
8503 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8505 if (!stored_player[i].active || &stored_player[i] == player)
8508 min_x = MIN(min_x, jx);
8509 min_y = MIN(min_y, jy);
8510 max_x = MAX(max_x, jx);
8511 max_y = MAX(max_y, jy);
8514 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
8517 static boolean AllPlayersInVisibleScreen()
8521 for (i = 0; i < MAX_PLAYERS; i++)
8523 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8525 if (!stored_player[i].active)
8528 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8535 void ScrollLevel(int dx, int dy)
8537 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
8540 BlitBitmap(drawto_field, drawto_field,
8541 FX + TILEX * (dx == -1) - softscroll_offset,
8542 FY + TILEY * (dy == -1) - softscroll_offset,
8543 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
8544 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
8545 FX + TILEX * (dx == 1) - softscroll_offset,
8546 FY + TILEY * (dy == 1) - softscroll_offset);
8550 x = (dx == 1 ? BX1 : BX2);
8551 for (y = BY1; y <= BY2; y++)
8552 DrawScreenField(x, y);
8557 y = (dy == 1 ? BY1 : BY2);
8558 for (x = BX1; x <= BX2; x++)
8559 DrawScreenField(x, y);
8562 redraw_mask |= REDRAW_FIELD;
8565 static boolean canFallDown(struct PlayerInfo *player)
8567 int jx = player->jx, jy = player->jy;
8569 return (IN_LEV_FIELD(jx, jy + 1) &&
8570 (IS_FREE(jx, jy + 1) ||
8571 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
8572 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
8573 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
8576 static boolean canPassField(int x, int y, int move_dir)
8578 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8579 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8580 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8583 int element = Feld[x][y];
8585 return (IS_PASSABLE_FROM(element, opposite_dir) &&
8586 !CAN_MOVE(element) &&
8587 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
8588 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
8589 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
8592 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
8594 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8595 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8596 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8600 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
8601 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
8602 (IS_DIGGABLE(Feld[newx][newy]) ||
8603 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
8604 canPassField(newx, newy, move_dir)));
8607 static void CheckGravityMovement(struct PlayerInfo *player)
8609 if (game.gravity && !player->programmed_action)
8611 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
8612 int move_dir_vertical = player->effective_action & MV_VERTICAL;
8613 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
8614 int jx = player->jx, jy = player->jy;
8615 boolean player_is_moving_to_valid_field =
8616 (!player_is_snapping &&
8617 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
8618 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
8619 boolean player_can_fall_down = canFallDown(player);
8621 if (player_can_fall_down &&
8622 !player_is_moving_to_valid_field)
8623 player->programmed_action = MV_DOWN;
8627 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
8629 return CheckGravityMovement(player);
8631 if (game.gravity && !player->programmed_action)
8633 int jx = player->jx, jy = player->jy;
8634 boolean field_under_player_is_free =
8635 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
8636 boolean player_is_standing_on_valid_field =
8637 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
8638 (IS_WALKABLE(Feld[jx][jy]) &&
8639 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
8641 if (field_under_player_is_free && !player_is_standing_on_valid_field)
8642 player->programmed_action = MV_DOWN;
8648 -----------------------------------------------------------------------------
8649 dx, dy: direction (non-diagonal) to try to move the player to
8650 real_dx, real_dy: direction as read from input device (can be diagonal)
8653 boolean MovePlayerOneStep(struct PlayerInfo *player,
8654 int dx, int dy, int real_dx, int real_dy)
8656 int jx = player->jx, jy = player->jy;
8657 int new_jx = jx + dx, new_jy = jy + dy;
8661 if (!player->active || (!dx && !dy))
8662 return MF_NO_ACTION;
8664 player->MovDir = (dx < 0 ? MV_LEFT :
8667 dy > 0 ? MV_DOWN : MV_NONE);
8669 if (!IN_LEV_FIELD(new_jx, new_jy))
8670 return MF_NO_ACTION;
8672 if (player->cannot_move)
8675 if (player->MovPos == 0)
8677 player->is_moving = FALSE;
8678 player->is_digging = FALSE;
8679 player->is_collecting = FALSE;
8680 player->is_snapping = FALSE;
8681 player->is_pushing = FALSE;
8684 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8685 SnapField(player, 0, 0);
8688 return MF_NO_ACTION;
8691 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
8692 return MF_NO_ACTION;
8694 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
8696 if (DONT_RUN_INTO(element))
8698 if (element == EL_ACID && dx == 0 && dy == 1)
8700 SplashAcid(new_jx, new_jy);
8701 Feld[jx][jy] = EL_PLAYER_1;
8702 InitMovingField(jx, jy, MV_DOWN);
8703 Store[jx][jy] = EL_ACID;
8704 ContinueMoving(jx, jy);
8708 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
8713 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
8714 if (can_move != MF_MOVING)
8717 /* check if DigField() has caused relocation of the player */
8718 if (player->jx != jx || player->jy != jy)
8719 return MF_NO_ACTION; /* <-- !!! CHECK THIS [-> MF_ACTION ?] !!! */
8721 StorePlayer[jx][jy] = 0;
8722 player->last_jx = jx;
8723 player->last_jy = jy;
8724 player->jx = new_jx;
8725 player->jy = new_jy;
8726 StorePlayer[new_jx][new_jy] = player->element_nr;
8728 if (player->move_delay_value_next != -1)
8730 player->move_delay_value = player->move_delay_value_next;
8731 player->move_delay_value_next = -1;
8735 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
8737 player->step_counter++;
8739 PlayerVisit[jx][jy] = FrameCounter;
8741 ScrollPlayer(player, SCROLL_INIT);
8746 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
8748 int jx = player->jx, jy = player->jy;
8749 int old_jx = jx, old_jy = jy;
8750 int moved = MF_NO_ACTION;
8752 if (!player->active)
8757 if (player->MovPos == 0)
8759 player->is_moving = FALSE;
8760 player->is_digging = FALSE;
8761 player->is_collecting = FALSE;
8762 player->is_snapping = FALSE;
8763 player->is_pushing = FALSE;
8769 if (player->move_delay > 0)
8772 player->move_delay = -1; /* set to "uninitialized" value */
8774 /* store if player is automatically moved to next field */
8775 player->is_auto_moving = (player->programmed_action != MV_NONE);
8777 /* remove the last programmed player action */
8778 player->programmed_action = 0;
8782 /* should only happen if pre-1.2 tape recordings are played */
8783 /* this is only for backward compatibility */
8785 int original_move_delay_value = player->move_delay_value;
8788 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
8792 /* scroll remaining steps with finest movement resolution */
8793 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
8795 while (player->MovPos)
8797 ScrollPlayer(player, SCROLL_GO_ON);
8798 ScrollScreen(NULL, SCROLL_GO_ON);
8800 AdvanceFrameAndPlayerCounters(player->index_nr);
8806 player->move_delay_value = original_move_delay_value;
8809 if (player->last_move_dir & MV_HORIZONTAL)
8811 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
8812 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
8816 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
8817 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
8823 if (moved & MF_MOVING && !ScreenMovPos &&
8824 (player == local_player || !options.network))
8826 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
8827 int offset = (setup.scroll_delay ? 3 : 0);
8829 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8831 /* actual player has left the screen -- scroll in that direction */
8832 if (jx != old_jx) /* player has moved horizontally */
8833 scroll_x += (jx - old_jx);
8834 else /* player has moved vertically */
8835 scroll_y += (jy - old_jy);
8839 if (jx != old_jx) /* player has moved horizontally */
8841 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
8842 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
8843 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
8845 /* don't scroll over playfield boundaries */
8846 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
8847 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
8849 /* don't scroll more than one field at a time */
8850 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
8852 /* don't scroll against the player's moving direction */
8853 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
8854 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
8855 scroll_x = old_scroll_x;
8857 else /* player has moved vertically */
8859 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
8860 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
8861 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
8863 /* don't scroll over playfield boundaries */
8864 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
8865 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
8867 /* don't scroll more than one field at a time */
8868 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
8870 /* don't scroll against the player's moving direction */
8871 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
8872 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
8873 scroll_y = old_scroll_y;
8877 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
8879 if (!options.network && !AllPlayersInVisibleScreen())
8881 scroll_x = old_scroll_x;
8882 scroll_y = old_scroll_y;
8886 ScrollScreen(player, SCROLL_INIT);
8887 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
8892 player->StepFrame = 0;
8894 if (moved & MF_MOVING)
8896 if (old_jx != jx && old_jy == jy)
8897 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
8898 else if (old_jx == jx && old_jy != jy)
8899 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
8901 DrawLevelField(jx, jy); /* for "crumbled sand" */
8903 player->last_move_dir = player->MovDir;
8904 player->is_moving = TRUE;
8905 player->is_snapping = FALSE;
8906 player->is_switching = FALSE;
8907 player->is_dropping = FALSE;
8911 CheckGravityMovementWhenNotMoving(player);
8913 player->is_moving = FALSE;
8915 /* at this point, the player is allowed to move, but cannot move right now
8916 (e.g. because of something blocking the way) -- ensure that the player
8917 is also allowed to move in the next frame (in old versions before 3.1.1,
8918 the player was forced to wait again for eight frames before next try) */
8920 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
8921 player->move_delay = 0; /* allow direct movement in the next frame */
8924 if (player->move_delay == -1) /* not yet initialized by DigField() */
8925 player->move_delay = player->move_delay_value;
8927 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8929 TestIfPlayerTouchesBadThing(jx, jy);
8930 TestIfPlayerTouchesCustomElement(jx, jy);
8933 if (!player->active)
8934 RemovePlayer(player);
8939 void ScrollPlayer(struct PlayerInfo *player, int mode)
8941 int jx = player->jx, jy = player->jy;
8942 int last_jx = player->last_jx, last_jy = player->last_jy;
8943 int move_stepsize = TILEX / player->move_delay_value;
8945 #if USE_NEW_PLAYER_SPEED
8946 if (!player->active)
8949 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
8952 if (!player->active || player->MovPos == 0)
8956 if (mode == SCROLL_INIT)
8958 player->actual_frame_counter = FrameCounter;
8959 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8961 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
8962 Feld[last_jx][last_jy] == EL_EMPTY)
8964 int last_field_block_delay = 0; /* start with no blocking at all */
8965 int block_delay_adjustment = player->block_delay_adjustment;
8967 /* if player blocks last field, add delay for exactly one move */
8968 if (player->block_last_field)
8970 last_field_block_delay += player->move_delay_value;
8972 /* when blocking enabled, prevent moving up despite gravity */
8973 if (game.gravity && player->MovDir == MV_UP)
8974 block_delay_adjustment = -1;
8977 /* add block delay adjustment (also possible when not blocking) */
8978 last_field_block_delay += block_delay_adjustment;
8980 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
8981 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
8984 #if USE_NEW_PLAYER_SPEED
8985 if (player->MovPos != 0) /* player has not yet reached destination */
8991 else if (!FrameReached(&player->actual_frame_counter, 1))
8995 printf("::: player->MovPos: %d -> %d\n",
8997 player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
9000 #if USE_NEW_PLAYER_SPEED
9001 if (player->MovPos != 0)
9003 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
9004 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9006 /* before DrawPlayer() to draw correct player graphic for this case */
9007 if (player->MovPos == 0)
9008 CheckGravityMovement(player);
9011 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
9012 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9014 /* before DrawPlayer() to draw correct player graphic for this case */
9015 if (player->MovPos == 0)
9016 CheckGravityMovement(player);
9019 if (player->MovPos == 0) /* player reached destination field */
9022 printf("::: player reached destination field\n");
9025 if (player->move_delay_reset_counter > 0)
9027 player->move_delay_reset_counter--;
9029 if (player->move_delay_reset_counter == 0)
9031 /* continue with normal speed after quickly moving through gate */
9032 HALVE_PLAYER_SPEED(player);
9034 /* be able to make the next move without delay */
9035 player->move_delay = 0;
9039 player->last_jx = jx;
9040 player->last_jy = jy;
9042 if (Feld[jx][jy] == EL_EXIT_OPEN ||
9043 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
9044 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
9046 DrawPlayer(player); /* needed here only to cleanup last field */
9047 RemovePlayer(player);
9049 if (local_player->friends_still_needed == 0 ||
9050 IS_SP_ELEMENT(Feld[jx][jy]))
9051 player->LevelSolved = player->GameOver = TRUE;
9054 /* this breaks one level: "machine", level 000 */
9056 int move_direction = player->MovDir;
9057 int enter_side = MV_DIR_OPPOSITE(move_direction);
9058 int leave_side = move_direction;
9059 int old_jx = last_jx;
9060 int old_jy = last_jy;
9061 int old_element = Feld[old_jx][old_jy];
9062 int new_element = Feld[jx][jy];
9064 if (IS_CUSTOM_ELEMENT(old_element))
9065 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
9067 player->index_bit, leave_side);
9069 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
9071 player->index_bit, leave_side);
9073 if (IS_CUSTOM_ELEMENT(new_element))
9074 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
9075 player->index_bit, enter_side);
9077 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
9079 player->index_bit, enter_side);
9081 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
9082 CE_MOVE_OF_X, move_direction);
9085 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9087 TestIfPlayerTouchesBadThing(jx, jy);
9088 TestIfPlayerTouchesCustomElement(jx, jy);
9090 /* needed because pushed element has not yet reached its destination,
9091 so it would trigger a change event at its previous field location */
9092 if (!player->is_pushing)
9093 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
9095 if (!player->active)
9096 RemovePlayer(player);
9099 if (level.use_step_counter)
9109 if (TimeLeft <= 10 && setup.time_limit)
9110 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9112 DrawGameValue_Time(TimeLeft);
9114 if (!TimeLeft && setup.time_limit)
9115 for (i = 0; i < MAX_PLAYERS; i++)
9116 KillPlayer(&stored_player[i]);
9118 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9119 DrawGameValue_Time(TimePlayed);
9122 if (tape.single_step && tape.recording && !tape.pausing &&
9123 !player->programmed_action)
9124 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9128 void ScrollScreen(struct PlayerInfo *player, int mode)
9130 static unsigned long screen_frame_counter = 0;
9132 if (mode == SCROLL_INIT)
9134 /* set scrolling step size according to actual player's moving speed */
9135 ScrollStepSize = TILEX / player->move_delay_value;
9137 screen_frame_counter = FrameCounter;
9138 ScreenMovDir = player->MovDir;
9139 ScreenMovPos = player->MovPos;
9140 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9143 else if (!FrameReached(&screen_frame_counter, 1))
9148 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
9149 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9150 redraw_mask |= REDRAW_FIELD;
9153 ScreenMovDir = MV_NONE;
9156 void TestIfPlayerTouchesCustomElement(int x, int y)
9158 static int xy[4][2] =
9165 static int trigger_sides[4][2] =
9167 /* center side border side */
9168 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
9169 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
9170 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
9171 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
9173 static int touch_dir[4] =
9180 int center_element = Feld[x][y]; /* should always be non-moving! */
9183 for (i = 0; i < NUM_DIRECTIONS; i++)
9185 int xx = x + xy[i][0];
9186 int yy = y + xy[i][1];
9187 int center_side = trigger_sides[i][0];
9188 int border_side = trigger_sides[i][1];
9191 if (!IN_LEV_FIELD(xx, yy))
9194 if (IS_PLAYER(x, y))
9196 struct PlayerInfo *player = PLAYERINFO(x, y);
9198 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9199 border_element = Feld[xx][yy]; /* may be moving! */
9200 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9201 border_element = Feld[xx][yy];
9202 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
9203 border_element = MovingOrBlocked2Element(xx, yy);
9205 continue; /* center and border element do not touch */
9207 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
9208 player->index_bit, border_side);
9209 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
9210 CE_PLAYER_TOUCHES_X,
9211 player->index_bit, border_side);
9213 else if (IS_PLAYER(xx, yy))
9215 struct PlayerInfo *player = PLAYERINFO(xx, yy);
9217 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9219 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9220 continue; /* center and border element do not touch */
9223 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
9224 player->index_bit, center_side);
9225 CheckTriggeredElementChangeByPlayer(x, y, center_element,
9226 CE_PLAYER_TOUCHES_X,
9227 player->index_bit, center_side);
9233 void TestIfElementTouchesCustomElement(int x, int y)
9235 static int xy[4][2] =
9242 static int trigger_sides[4][2] =
9244 /* center side border side */
9245 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
9246 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
9247 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
9248 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
9250 static int touch_dir[4] =
9257 boolean change_center_element = FALSE;
9258 int center_element = Feld[x][y]; /* should always be non-moving! */
9261 for (i = 0; i < NUM_DIRECTIONS; i++)
9263 int xx = x + xy[i][0];
9264 int yy = y + xy[i][1];
9265 int center_side = trigger_sides[i][0];
9266 int border_side = trigger_sides[i][1];
9269 if (!IN_LEV_FIELD(xx, yy))
9272 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9273 border_element = Feld[xx][yy]; /* may be moving! */
9274 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9275 border_element = Feld[xx][yy];
9276 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
9277 border_element = MovingOrBlocked2Element(xx, yy);
9279 continue; /* center and border element do not touch */
9281 /* check for change of center element (but change it only once) */
9282 if (!change_center_element)
9283 change_center_element =
9284 CheckElementChangeBySide(x, y, center_element, border_element,
9285 CE_TOUCHING_X, border_side);
9287 /* check for change of border element */
9288 CheckElementChangeBySide(xx, yy, border_element, center_element,
9289 CE_TOUCHING_X, center_side);
9293 void TestIfElementHitsCustomElement(int x, int y, int direction)
9295 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9296 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9297 int hitx = x + dx, hity = y + dy;
9298 int hitting_element = Feld[x][y];
9299 int touched_element;
9301 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9304 touched_element = (IN_LEV_FIELD(hitx, hity) ?
9305 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
9307 if (IN_LEV_FIELD(hitx, hity))
9309 int opposite_direction = MV_DIR_OPPOSITE(direction);
9310 int hitting_side = direction;
9311 int touched_side = opposite_direction;
9312 boolean object_hit = (!IS_MOVING(hitx, hity) ||
9313 MovDir[hitx][hity] != direction ||
9314 ABS(MovPos[hitx][hity]) <= TILEY / 2);
9320 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9321 CE_HITTING_X, touched_side);
9323 CheckElementChangeBySide(hitx, hity, touched_element,
9324 hitting_element, CE_HIT_BY_X, hitting_side);
9326 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9327 CE_HIT_BY_SOMETHING, opposite_direction);
9331 /* "hitting something" is also true when hitting the playfield border */
9332 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9333 CE_HITTING_SOMETHING, direction);
9337 void TestIfElementSmashesCustomElement(int x, int y, int direction)
9339 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9340 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9341 int hitx = x + dx, hity = y + dy;
9342 int hitting_element = Feld[x][y];
9343 int touched_element;
9345 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
9346 !IS_FREE(hitx, hity) &&
9347 (!IS_MOVING(hitx, hity) ||
9348 MovDir[hitx][hity] != direction ||
9349 ABS(MovPos[hitx][hity]) <= TILEY / 2));
9352 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9356 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
9360 touched_element = (IN_LEV_FIELD(hitx, hity) ?
9361 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
9363 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9364 EP_CAN_SMASH_EVERYTHING, direction);
9366 if (IN_LEV_FIELD(hitx, hity))
9368 int opposite_direction = MV_DIR_OPPOSITE(direction);
9369 int hitting_side = direction;
9370 int touched_side = opposite_direction;
9372 int touched_element = MovingOrBlocked2Element(hitx, hity);
9375 boolean object_hit = (!IS_MOVING(hitx, hity) ||
9376 MovDir[hitx][hity] != direction ||
9377 ABS(MovPos[hitx][hity]) <= TILEY / 2);
9386 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9387 CE_SMASHED_BY_SOMETHING, opposite_direction);
9389 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9390 CE_OTHER_IS_SMASHING, touched_side);
9392 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9393 CE_OTHER_GETS_SMASHED, hitting_side);
9399 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
9401 int i, kill_x = -1, kill_y = -1;
9402 int bad_element = -1;
9403 static int test_xy[4][2] =
9410 static int test_dir[4] =
9418 for (i = 0; i < NUM_DIRECTIONS; i++)
9420 int test_x, test_y, test_move_dir, test_element;
9422 test_x = good_x + test_xy[i][0];
9423 test_y = good_y + test_xy[i][1];
9425 if (!IN_LEV_FIELD(test_x, test_y))
9429 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
9431 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
9433 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
9434 2nd case: DONT_TOUCH style bad thing does not move away from good thing
9436 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
9437 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
9441 bad_element = test_element;
9447 if (kill_x != -1 || kill_y != -1)
9449 if (IS_PLAYER(good_x, good_y))
9451 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
9453 if (player->shield_deadly_time_left > 0 &&
9454 !IS_INDESTRUCTIBLE(bad_element))
9455 Bang(kill_x, kill_y);
9456 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
9460 Bang(good_x, good_y);
9464 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
9466 int i, kill_x = -1, kill_y = -1;
9467 int bad_element = Feld[bad_x][bad_y];
9468 static int test_xy[4][2] =
9475 static int touch_dir[4] =
9482 static int test_dir[4] =
9490 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
9493 for (i = 0; i < NUM_DIRECTIONS; i++)
9495 int test_x, test_y, test_move_dir, test_element;
9497 test_x = bad_x + test_xy[i][0];
9498 test_y = bad_y + test_xy[i][1];
9499 if (!IN_LEV_FIELD(test_x, test_y))
9503 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
9505 test_element = Feld[test_x][test_y];
9507 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
9508 2nd case: DONT_TOUCH style bad thing does not move away from good thing
9510 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
9511 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
9513 /* good thing is player or penguin that does not move away */
9514 if (IS_PLAYER(test_x, test_y))
9516 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
9518 if (bad_element == EL_ROBOT && player->is_moving)
9519 continue; /* robot does not kill player if he is moving */
9521 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9523 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9524 continue; /* center and border element do not touch */
9531 else if (test_element == EL_PENGUIN)
9540 if (kill_x != -1 || kill_y != -1)
9542 if (IS_PLAYER(kill_x, kill_y))
9544 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
9546 if (player->shield_deadly_time_left > 0 &&
9547 !IS_INDESTRUCTIBLE(bad_element))
9549 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
9553 Bang(kill_x, kill_y);
9557 void TestIfPlayerTouchesBadThing(int x, int y)
9559 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
9562 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
9564 TestIfGoodThingHitsBadThing(x, y, move_dir);
9567 void TestIfBadThingTouchesPlayer(int x, int y)
9569 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
9572 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
9574 TestIfBadThingHitsGoodThing(x, y, move_dir);
9577 void TestIfFriendTouchesBadThing(int x, int y)
9579 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
9582 void TestIfBadThingTouchesFriend(int x, int y)
9584 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
9587 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
9589 int i, kill_x = bad_x, kill_y = bad_y;
9590 static int xy[4][2] =
9598 for (i = 0; i < NUM_DIRECTIONS; i++)
9602 x = bad_x + xy[i][0];
9603 y = bad_y + xy[i][1];
9604 if (!IN_LEV_FIELD(x, y))
9607 element = Feld[x][y];
9608 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
9609 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
9617 if (kill_x != bad_x || kill_y != bad_y)
9621 void KillPlayer(struct PlayerInfo *player)
9623 int jx = player->jx, jy = player->jy;
9625 if (!player->active)
9628 /* remove accessible field at the player's position */
9629 Feld[jx][jy] = EL_EMPTY;
9631 /* deactivate shield (else Bang()/Explode() would not work right) */
9632 player->shield_normal_time_left = 0;
9633 player->shield_deadly_time_left = 0;
9639 static void KillPlayerUnlessEnemyProtected(int x, int y)
9641 if (!PLAYER_ENEMY_PROTECTED(x, y))
9642 KillPlayer(PLAYERINFO(x, y));
9645 static void KillPlayerUnlessExplosionProtected(int x, int y)
9647 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
9648 KillPlayer(PLAYERINFO(x, y));
9651 void BuryPlayer(struct PlayerInfo *player)
9653 int jx = player->jx, jy = player->jy;
9655 if (!player->active)
9658 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
9659 PlayLevelSound(jx, jy, SND_GAME_LOSING);
9661 player->GameOver = TRUE;
9662 RemovePlayer(player);
9665 void RemovePlayer(struct PlayerInfo *player)
9667 int jx = player->jx, jy = player->jy;
9668 int i, found = FALSE;
9670 player->present = FALSE;
9671 player->active = FALSE;
9673 if (!ExplodeField[jx][jy])
9674 StorePlayer[jx][jy] = 0;
9676 if (player->is_moving)
9677 DrawLevelField(player->last_jx, player->last_jy);
9679 for (i = 0; i < MAX_PLAYERS; i++)
9680 if (stored_player[i].active)
9684 AllPlayersGone = TRUE;
9690 #if USE_NEW_SNAP_DELAY
9691 static void setFieldForSnapping(int x, int y, int element, int direction)
9693 struct ElementInfo *ei = &element_info[element];
9694 int direction_bit = MV_DIR_BIT(direction);
9695 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
9696 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
9697 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
9699 Feld[x][y] = EL_ELEMENT_SNAPPING;
9700 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
9702 ResetGfxAnimation(x, y);
9704 GfxElement[x][y] = element;
9705 GfxAction[x][y] = action;
9706 GfxDir[x][y] = direction;
9707 GfxFrame[x][y] = -1;
9712 =============================================================================
9713 checkDiagonalPushing()
9714 -----------------------------------------------------------------------------
9715 check if diagonal input device direction results in pushing of object
9716 (by checking if the alternative direction is walkable, diggable, ...)
9717 =============================================================================
9720 static boolean checkDiagonalPushing(struct PlayerInfo *player,
9721 int x, int y, int real_dx, int real_dy)
9723 int jx, jy, dx, dy, xx, yy;
9725 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
9728 /* diagonal direction: check alternative direction */
9733 xx = jx + (dx == 0 ? real_dx : 0);
9734 yy = jy + (dy == 0 ? real_dy : 0);
9736 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
9740 =============================================================================
9742 -----------------------------------------------------------------------------
9743 x, y: field next to player (non-diagonal) to try to dig to
9744 real_dx, real_dy: direction as read from input device (can be diagonal)
9745 =============================================================================
9748 int DigField(struct PlayerInfo *player,
9749 int oldx, int oldy, int x, int y,
9750 int real_dx, int real_dy, int mode)
9752 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
9753 boolean player_was_pushing = player->is_pushing;
9754 int jx = oldx, jy = oldy;
9755 int dx = x - jx, dy = y - jy;
9756 int nextx = x + dx, nexty = y + dy;
9757 int move_direction = (dx == -1 ? MV_LEFT :
9758 dx == +1 ? MV_RIGHT :
9760 dy == +1 ? MV_DOWN : MV_NONE);
9761 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
9762 int dig_side = MV_DIR_OPPOSITE(move_direction);
9763 int old_element = Feld[jx][jy];
9767 if (is_player) /* function can also be called by EL_PENGUIN */
9769 if (player->MovPos == 0)
9771 player->is_digging = FALSE;
9772 player->is_collecting = FALSE;
9775 if (player->MovPos == 0) /* last pushing move finished */
9776 player->is_pushing = FALSE;
9778 if (mode == DF_NO_PUSH) /* player just stopped pushing */
9780 player->is_switching = FALSE;
9781 player->push_delay = -1;
9783 return MF_NO_ACTION;
9787 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
9788 return MF_NO_ACTION;
9790 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
9791 old_element = Back[jx][jy];
9793 /* in case of element dropped at player position, check background */
9794 else if (Back[jx][jy] != EL_EMPTY &&
9795 game.engine_version >= VERSION_IDENT(2,2,0,0))
9796 old_element = Back[jx][jy];
9798 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
9799 return MF_NO_ACTION; /* field has no opening in this direction */
9801 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
9802 return MF_NO_ACTION; /* field has no opening in this direction */
9804 element = Feld[x][y];
9805 #if USE_NEW_CUSTOM_VALUE
9808 collect_count = element_info[element].collect_count_initial;
9810 collect_count = CustomValue[x][y];
9814 collect_count = element_info[element].collect_count_initial;
9818 if (element != EL_BLOCKED &&
9819 CustomValue[x][y] != element_info[element].collect_count_initial)
9820 printf("::: %d: %d != %d\n",
9823 element_info[element].collect_count_initial);
9826 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
9827 return MF_NO_ACTION;
9829 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
9830 game.engine_version >= VERSION_IDENT(2,2,0,0))
9832 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
9833 player->index_bit, dig_side);
9834 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
9835 player->index_bit, dig_side);
9837 if (Feld[x][y] != element) /* field changed by snapping */
9840 return MF_NO_ACTION;
9843 if (game.gravity && is_player && !player->is_auto_moving &&
9844 canFallDown(player) && move_direction != MV_DOWN &&
9845 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
9846 return MF_NO_ACTION; /* player cannot walk here due to gravity */
9848 if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
9850 int sound_element = SND_ELEMENT(element);
9851 int sound_action = ACTION_WALKING;
9853 if (IS_RND_GATE(element))
9855 if (!player->key[RND_GATE_NR(element)])
9856 return MF_NO_ACTION;
9858 else if (IS_RND_GATE_GRAY(element))
9860 if (!player->key[RND_GATE_GRAY_NR(element)])
9861 return MF_NO_ACTION;
9863 else if (IS_RND_GATE_GRAY_ACTIVE(element))
9865 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
9866 return MF_NO_ACTION;
9868 else if (element == EL_EXIT_OPEN ||
9869 element == EL_SP_EXIT_OPEN ||
9870 element == EL_SP_EXIT_OPENING)
9872 sound_action = ACTION_PASSING; /* player is passing exit */
9874 else if (element == EL_EMPTY)
9876 sound_action = ACTION_MOVING; /* nothing to walk on */
9879 /* play sound from background or player, whatever is available */
9880 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
9881 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
9883 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
9885 else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
9887 if (!ACCESS_FROM(element, opposite_direction))
9888 return MF_NO_ACTION; /* field not accessible from this direction */
9890 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
9891 return MF_NO_ACTION;
9893 if (IS_EM_GATE(element))
9895 if (!player->key[EM_GATE_NR(element)])
9896 return MF_NO_ACTION;
9898 else if (IS_EM_GATE_GRAY(element))
9900 if (!player->key[EM_GATE_GRAY_NR(element)])
9901 return MF_NO_ACTION;
9903 else if (IS_EM_GATE_GRAY_ACTIVE(element))
9905 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
9906 return MF_NO_ACTION;
9908 else if (IS_SP_PORT(element))
9910 if (element == EL_SP_GRAVITY_PORT_LEFT ||
9911 element == EL_SP_GRAVITY_PORT_RIGHT ||
9912 element == EL_SP_GRAVITY_PORT_UP ||
9913 element == EL_SP_GRAVITY_PORT_DOWN)
9914 game.gravity = !game.gravity;
9915 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
9916 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
9917 element == EL_SP_GRAVITY_ON_PORT_UP ||
9918 element == EL_SP_GRAVITY_ON_PORT_DOWN)
9919 game.gravity = TRUE;
9920 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
9921 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
9922 element == EL_SP_GRAVITY_OFF_PORT_UP ||
9923 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
9924 game.gravity = FALSE;
9927 /* automatically move to the next field with double speed */
9928 player->programmed_action = move_direction;
9930 if (player->move_delay_reset_counter == 0)
9932 player->move_delay_reset_counter = 2; /* two double speed steps */
9934 DOUBLE_PLAYER_SPEED(player);
9937 PlayLevelSoundAction(x, y, ACTION_PASSING);
9939 else if (IS_DIGGABLE(element))
9943 if (mode != DF_SNAP)
9945 GfxElement[x][y] = GFX_ELEMENT(element);
9946 player->is_digging = TRUE;
9949 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
9951 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
9952 player->index_bit, dig_side);
9954 if (mode == DF_SNAP)
9956 #if USE_NEW_SNAP_DELAY
9957 if (level.block_snap_field)
9958 setFieldForSnapping(x, y, element, move_direction);
9960 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9962 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9965 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
9966 player->index_bit, dig_side);
9969 else if (IS_COLLECTIBLE(element))
9973 if (is_player && mode != DF_SNAP)
9975 GfxElement[x][y] = element;
9976 player->is_collecting = TRUE;
9979 if (element == EL_SPEED_PILL)
9981 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
9983 else if (element == EL_EXTRA_TIME && level.time > 0)
9985 TimeLeft += level.extra_time;
9986 DrawGameValue_Time(TimeLeft);
9988 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
9990 player->shield_normal_time_left += level.shield_normal_time;
9991 if (element == EL_SHIELD_DEADLY)
9992 player->shield_deadly_time_left += level.shield_deadly_time;
9994 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
9996 if (player->inventory_size < MAX_INVENTORY_SIZE)
9997 player->inventory_element[player->inventory_size++] = element;
9999 DrawGameValue_Dynamite(local_player->inventory_size);
10001 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
10003 player->dynabomb_count++;
10004 player->dynabombs_left++;
10006 else if (element == EL_DYNABOMB_INCREASE_SIZE)
10008 player->dynabomb_size++;
10010 else if (element == EL_DYNABOMB_INCREASE_POWER)
10012 player->dynabomb_xl = TRUE;
10014 else if (IS_KEY(element))
10016 player->key[KEY_NR(element)] = TRUE;
10018 DrawGameValue_Keys(player->key);
10020 redraw_mask |= REDRAW_DOOR_1;
10022 else if (IS_ENVELOPE(element))
10024 player->show_envelope = element;
10026 else if (element == EL_EMC_LENSES)
10028 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
10030 RedrawAllInvisibleElementsForLenses();
10032 else if (element == EL_EMC_MAGNIFIER)
10034 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
10036 RedrawAllInvisibleElementsForMagnifier();
10038 else if (IS_DROPPABLE(element) ||
10039 IS_THROWABLE(element)) /* can be collected and dropped */
10043 if (collect_count == 0)
10044 player->inventory_infinite_element = element;
10046 for (i = 0; i < collect_count; i++)
10047 if (player->inventory_size < MAX_INVENTORY_SIZE)
10048 player->inventory_element[player->inventory_size++] = element;
10050 DrawGameValue_Dynamite(local_player->inventory_size);
10052 else if (collect_count > 0)
10054 local_player->gems_still_needed -= collect_count;
10055 if (local_player->gems_still_needed < 0)
10056 local_player->gems_still_needed = 0;
10058 DrawGameValue_Emeralds(local_player->gems_still_needed);
10061 RaiseScoreElement(element);
10062 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
10065 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
10066 player->index_bit, dig_side);
10068 if (mode == DF_SNAP)
10070 #if USE_NEW_SNAP_DELAY
10071 if (level.block_snap_field)
10072 setFieldForSnapping(x, y, element, move_direction);
10074 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10076 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10079 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
10080 player->index_bit, dig_side);
10083 else if (IS_PUSHABLE(element))
10085 if (mode == DF_SNAP && element != EL_BD_ROCK)
10086 return MF_NO_ACTION;
10088 if (CAN_FALL(element) && dy)
10089 return MF_NO_ACTION;
10091 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
10092 !(element == EL_SPRING && level.use_spring_bug))
10093 return MF_NO_ACTION;
10095 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
10096 ((move_direction & MV_VERTICAL &&
10097 ((element_info[element].move_pattern & MV_LEFT &&
10098 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
10099 (element_info[element].move_pattern & MV_RIGHT &&
10100 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
10101 (move_direction & MV_HORIZONTAL &&
10102 ((element_info[element].move_pattern & MV_UP &&
10103 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
10104 (element_info[element].move_pattern & MV_DOWN &&
10105 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
10106 return MF_NO_ACTION;
10108 /* do not push elements already moving away faster than player */
10109 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
10110 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
10111 return MF_NO_ACTION;
10113 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
10115 if (player->push_delay_value == -1 || !player_was_pushing)
10116 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10118 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
10120 if (player->push_delay_value == -1)
10121 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10123 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
10125 if (!player->is_pushing)
10126 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10129 player->is_pushing = TRUE;
10131 if (!(IN_LEV_FIELD(nextx, nexty) &&
10132 (IS_FREE(nextx, nexty) ||
10133 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
10134 IS_SB_ELEMENT(element)))))
10135 return MF_NO_ACTION;
10137 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
10138 return MF_NO_ACTION;
10140 if (player->push_delay == -1) /* new pushing; restart delay */
10141 player->push_delay = 0;
10143 if (player->push_delay < player->push_delay_value &&
10144 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
10145 element != EL_SPRING && element != EL_BALLOON)
10147 /* make sure that there is no move delay before next try to push */
10148 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
10149 player->move_delay = 0;
10151 return MF_NO_ACTION;
10154 if (IS_SB_ELEMENT(element))
10156 if (element == EL_SOKOBAN_FIELD_FULL)
10158 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
10159 local_player->sokobanfields_still_needed++;
10162 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
10164 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
10165 local_player->sokobanfields_still_needed--;
10168 Feld[x][y] = EL_SOKOBAN_OBJECT;
10170 if (Back[x][y] == Back[nextx][nexty])
10171 PlayLevelSoundAction(x, y, ACTION_PUSHING);
10172 else if (Back[x][y] != 0)
10173 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
10176 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
10179 if (local_player->sokobanfields_still_needed == 0 &&
10180 game.emulation == EMU_SOKOBAN)
10182 player->LevelSolved = player->GameOver = TRUE;
10183 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
10187 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
10189 InitMovingField(x, y, move_direction);
10190 GfxAction[x][y] = ACTION_PUSHING;
10192 if (mode == DF_SNAP)
10193 ContinueMoving(x, y);
10195 MovPos[x][y] = (dx != 0 ? dx : dy);
10197 Pushed[x][y] = TRUE;
10198 Pushed[nextx][nexty] = TRUE;
10200 if (game.engine_version < VERSION_IDENT(2,2,0,7))
10201 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10203 player->push_delay_value = -1; /* get new value later */
10205 /* check for element change _after_ element has been pushed */
10206 if (game.use_change_when_pushing_bug)
10208 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
10209 player->index_bit, dig_side);
10210 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
10211 player->index_bit, dig_side);
10214 else if (IS_SWITCHABLE(element))
10216 if (PLAYER_SWITCHING(player, x, y))
10218 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
10219 player->index_bit, dig_side);
10224 player->is_switching = TRUE;
10225 player->switch_x = x;
10226 player->switch_y = y;
10228 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
10230 if (element == EL_ROBOT_WHEEL)
10232 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
10236 DrawLevelField(x, y);
10238 else if (element == EL_SP_TERMINAL)
10242 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
10244 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
10246 else if (Feld[xx][yy] == EL_SP_TERMINAL)
10247 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
10250 else if (IS_BELT_SWITCH(element))
10252 ToggleBeltSwitch(x, y);
10254 else if (element == EL_SWITCHGATE_SWITCH_UP ||
10255 element == EL_SWITCHGATE_SWITCH_DOWN)
10257 ToggleSwitchgateSwitch(x, y);
10259 else if (element == EL_LIGHT_SWITCH ||
10260 element == EL_LIGHT_SWITCH_ACTIVE)
10262 ToggleLightSwitch(x, y);
10264 else if (element == EL_TIMEGATE_SWITCH)
10266 ActivateTimegateSwitch(x, y);
10268 else if (element == EL_BALLOON_SWITCH_LEFT ||
10269 element == EL_BALLOON_SWITCH_RIGHT ||
10270 element == EL_BALLOON_SWITCH_UP ||
10271 element == EL_BALLOON_SWITCH_DOWN ||
10272 element == EL_BALLOON_SWITCH_NONE ||
10273 element == EL_BALLOON_SWITCH_ANY)
10275 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
10276 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
10277 element == EL_BALLOON_SWITCH_UP ? MV_UP :
10278 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
10279 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
10282 else if (element == EL_LAMP)
10284 Feld[x][y] = EL_LAMP_ACTIVE;
10285 local_player->lights_still_needed--;
10287 ResetGfxAnimation(x, y);
10288 DrawLevelField(x, y);
10290 else if (element == EL_TIME_ORB_FULL)
10292 Feld[x][y] = EL_TIME_ORB_EMPTY;
10294 if (level.time > 0 || level.use_time_orb_bug)
10296 TimeLeft += level.time_orb_time;
10297 DrawGameValue_Time(TimeLeft);
10300 ResetGfxAnimation(x, y);
10301 DrawLevelField(x, y);
10304 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
10305 player->index_bit, dig_side);
10307 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
10308 player->index_bit, dig_side);
10310 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
10311 player->index_bit, dig_side);
10317 if (!PLAYER_SWITCHING(player, x, y))
10319 player->is_switching = TRUE;
10320 player->switch_x = x;
10321 player->switch_y = y;
10323 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
10324 player->index_bit, dig_side);
10325 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
10326 player->index_bit, dig_side);
10328 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
10329 player->index_bit, dig_side);
10330 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
10331 player->index_bit, dig_side);
10334 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
10335 player->index_bit, dig_side);
10336 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
10337 player->index_bit, dig_side);
10339 return MF_NO_ACTION;
10342 player->push_delay = -1;
10344 if (is_player) /* function can also be called by EL_PENGUIN */
10346 if (Feld[x][y] != element) /* really digged/collected something */
10347 player->is_collecting = !player->is_digging;
10353 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
10355 int jx = player->jx, jy = player->jy;
10356 int x = jx + dx, y = jy + dy;
10357 int snap_direction = (dx == -1 ? MV_LEFT :
10358 dx == +1 ? MV_RIGHT :
10360 dy == +1 ? MV_DOWN : MV_NONE);
10362 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
10365 if (!player->active || !IN_LEV_FIELD(x, y))
10373 if (player->MovPos == 0)
10374 player->is_pushing = FALSE;
10376 player->is_snapping = FALSE;
10378 if (player->MovPos == 0)
10380 player->is_moving = FALSE;
10381 player->is_digging = FALSE;
10382 player->is_collecting = FALSE;
10388 if (player->is_snapping)
10391 player->MovDir = snap_direction;
10393 if (player->MovPos == 0)
10395 player->is_moving = FALSE;
10396 player->is_digging = FALSE;
10397 player->is_collecting = FALSE;
10400 player->is_dropping = FALSE;
10402 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
10405 player->is_snapping = TRUE;
10407 if (player->MovPos == 0)
10409 player->is_moving = FALSE;
10410 player->is_digging = FALSE;
10411 player->is_collecting = FALSE;
10414 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
10415 DrawLevelField(player->last_jx, player->last_jy);
10417 DrawLevelField(x, y);
10422 boolean DropElement(struct PlayerInfo *player)
10424 int old_element, new_element;
10425 int dropx = player->jx, dropy = player->jy;
10426 int drop_direction = player->MovDir;
10427 int drop_side = drop_direction;
10428 int drop_element = (player->inventory_size > 0 ?
10429 player->inventory_element[player->inventory_size - 1] :
10430 player->inventory_infinite_element != EL_UNDEFINED ?
10431 player->inventory_infinite_element :
10432 player->dynabombs_left > 0 ?
10433 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
10436 /* do not drop an element on top of another element; when holding drop key
10437 pressed without moving, dropped element must move away before the next
10438 element can be dropped (this is especially important if the next element
10439 is dynamite, which can be placed on background for historical reasons) */
10440 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
10443 if (IS_THROWABLE(drop_element))
10445 dropx += GET_DX_FROM_DIR(drop_direction);
10446 dropy += GET_DY_FROM_DIR(drop_direction);
10448 if (!IN_LEV_FIELD(dropx, dropy))
10452 old_element = Feld[dropx][dropy]; /* old element at dropping position */
10453 new_element = drop_element; /* default: no change when dropping */
10455 /* check if player is active, not moving and ready to drop */
10456 if (!player->active || player->MovPos || player->drop_delay > 0)
10459 /* check if player has anything that can be dropped */
10460 if (new_element == EL_UNDEFINED)
10463 /* check if anything can be dropped at the current position */
10464 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
10467 /* collected custom elements can only be dropped on empty fields */
10468 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
10471 if (old_element != EL_EMPTY)
10472 Back[dropx][dropy] = old_element; /* store old element on this field */
10474 ResetGfxAnimation(dropx, dropy);
10475 ResetRandomAnimationValue(dropx, dropy);
10477 if (player->inventory_size > 0 ||
10478 player->inventory_infinite_element != EL_UNDEFINED)
10480 if (player->inventory_size > 0)
10482 player->inventory_size--;
10484 DrawGameValue_Dynamite(local_player->inventory_size);
10486 if (new_element == EL_DYNAMITE)
10487 new_element = EL_DYNAMITE_ACTIVE;
10488 else if (new_element == EL_SP_DISK_RED)
10489 new_element = EL_SP_DISK_RED_ACTIVE;
10492 Feld[dropx][dropy] = new_element;
10494 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
10495 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
10496 el2img(Feld[dropx][dropy]), 0);
10498 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
10500 /* needed if previous element just changed to "empty" in the last frame */
10501 Changed[dropx][dropy] = 0; /* allow at least one more change */
10503 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
10504 player->index_bit, drop_side);
10505 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
10507 player->index_bit, drop_side);
10509 TestIfElementTouchesCustomElement(dropx, dropy);
10511 else /* player is dropping a dyna bomb */
10513 player->dynabombs_left--;
10515 Feld[dropx][dropy] = new_element;
10517 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
10518 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
10519 el2img(Feld[dropx][dropy]), 0);
10521 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
10524 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
10525 InitField_WithBug1(dropx, dropy, FALSE);
10527 new_element = Feld[dropx][dropy]; /* element might have changed */
10529 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
10530 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
10532 int move_direction, nextx, nexty;
10534 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
10535 MovDir[dropx][dropy] = drop_direction;
10537 move_direction = MovDir[dropx][dropy];
10538 nextx = dropx + GET_DX_FROM_DIR(move_direction);
10539 nexty = dropy + GET_DY_FROM_DIR(move_direction);
10541 Changed[dropx][dropy] = 0; /* allow at least one more change */
10542 CheckCollision[dropx][dropy] = 2;
10545 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
10546 player->is_dropping = TRUE;
10548 player->drop_x = dropx;
10549 player->drop_y = dropy;
10554 /* ------------------------------------------------------------------------- */
10555 /* game sound playing functions */
10556 /* ------------------------------------------------------------------------- */
10558 static int *loop_sound_frame = NULL;
10559 static int *loop_sound_volume = NULL;
10561 void InitPlayLevelSound()
10563 int num_sounds = getSoundListSize();
10565 checked_free(loop_sound_frame);
10566 checked_free(loop_sound_volume);
10568 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
10569 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
10572 static void PlayLevelSound(int x, int y, int nr)
10574 int sx = SCREENX(x), sy = SCREENY(y);
10575 int volume, stereo_position;
10576 int max_distance = 8;
10577 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
10579 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
10580 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
10583 if (!IN_LEV_FIELD(x, y) ||
10584 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
10585 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
10588 volume = SOUND_MAX_VOLUME;
10590 if (!IN_SCR_FIELD(sx, sy))
10592 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
10593 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
10595 volume -= volume * (dx > dy ? dx : dy) / max_distance;
10598 stereo_position = (SOUND_MAX_LEFT +
10599 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
10600 (SCR_FIELDX + 2 * max_distance));
10602 if (IS_LOOP_SOUND(nr))
10604 /* This assures that quieter loop sounds do not overwrite louder ones,
10605 while restarting sound volume comparison with each new game frame. */
10607 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
10610 loop_sound_volume[nr] = volume;
10611 loop_sound_frame[nr] = FrameCounter;
10614 PlaySoundExt(nr, volume, stereo_position, type);
10617 static void PlayLevelSoundNearest(int x, int y, int sound_action)
10619 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
10620 x > LEVELX(BX2) ? LEVELX(BX2) : x,
10621 y < LEVELY(BY1) ? LEVELY(BY1) :
10622 y > LEVELY(BY2) ? LEVELY(BY2) : y,
10626 static void PlayLevelSoundAction(int x, int y, int action)
10628 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
10631 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
10633 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
10635 if (sound_effect != SND_UNDEFINED)
10636 PlayLevelSound(x, y, sound_effect);
10639 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
10642 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
10644 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10645 PlayLevelSound(x, y, sound_effect);
10648 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
10650 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
10652 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10653 PlayLevelSound(x, y, sound_effect);
10656 static void StopLevelSoundActionIfLoop(int x, int y, int action)
10658 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
10660 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10661 StopSound(sound_effect);
10664 static void PlayLevelMusic()
10666 if (levelset.music[level_nr] != MUS_UNDEFINED)
10667 PlayMusic(levelset.music[level_nr]); /* from config file */
10669 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
10672 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
10674 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
10679 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
10683 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
10687 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10691 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10695 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
10699 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
10703 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
10706 case SAMPLE_android_clone:
10707 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
10710 case SAMPLE_android_move:
10711 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
10714 case SAMPLE_spring:
10715 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10719 PlayLevelSoundElementAction(x, y, element, ACTION_SLURPED_BY_SPRING);
10723 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
10726 case SAMPLE_eater_eat:
10727 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
10731 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
10734 case SAMPLE_collect:
10735 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
10738 case SAMPLE_diamond:
10739 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10742 case SAMPLE_squash:
10743 /* !!! CHECK THIS !!! */
10745 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
10747 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
10751 case SAMPLE_wonderfall:
10752 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
10756 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10760 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
10764 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
10768 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
10772 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
10776 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
10779 case SAMPLE_wonder:
10780 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
10784 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
10787 case SAMPLE_exit_open:
10788 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
10791 case SAMPLE_exit_leave:
10792 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
10795 case SAMPLE_dynamite:
10796 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
10800 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
10804 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
10808 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
10812 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
10816 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
10820 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10824 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
10829 void RaiseScore(int value)
10831 local_player->score += value;
10833 DrawGameValue_Score(local_player->score);
10836 void RaiseScoreElement(int element)
10841 case EL_BD_DIAMOND:
10842 case EL_EMERALD_YELLOW:
10843 case EL_EMERALD_RED:
10844 case EL_EMERALD_PURPLE:
10845 case EL_SP_INFOTRON:
10846 RaiseScore(level.score[SC_EMERALD]);
10849 RaiseScore(level.score[SC_DIAMOND]);
10852 RaiseScore(level.score[SC_CRYSTAL]);
10855 RaiseScore(level.score[SC_PEARL]);
10858 case EL_BD_BUTTERFLY:
10859 case EL_SP_ELECTRON:
10860 RaiseScore(level.score[SC_BUG]);
10863 case EL_BD_FIREFLY:
10864 case EL_SP_SNIKSNAK:
10865 RaiseScore(level.score[SC_SPACESHIP]);
10868 case EL_DARK_YAMYAM:
10869 RaiseScore(level.score[SC_YAMYAM]);
10872 RaiseScore(level.score[SC_ROBOT]);
10875 RaiseScore(level.score[SC_PACMAN]);
10878 RaiseScore(level.score[SC_NUT]);
10881 case EL_SP_DISK_RED:
10882 case EL_DYNABOMB_INCREASE_NUMBER:
10883 case EL_DYNABOMB_INCREASE_SIZE:
10884 case EL_DYNABOMB_INCREASE_POWER:
10885 RaiseScore(level.score[SC_DYNAMITE]);
10887 case EL_SHIELD_NORMAL:
10888 case EL_SHIELD_DEADLY:
10889 RaiseScore(level.score[SC_SHIELD]);
10891 case EL_EXTRA_TIME:
10892 RaiseScore(level.extra_time_score);
10906 RaiseScore(level.score[SC_KEY]);
10909 RaiseScore(element_info[element].collect_score);
10914 void RequestQuitGame(boolean ask_if_really_quit)
10916 if (AllPlayersGone ||
10917 !ask_if_really_quit ||
10918 level_editor_test_game ||
10919 Request("Do you really want to quit the game ?",
10920 REQ_ASK | REQ_STAY_CLOSED))
10922 #if defined(NETWORK_AVALIABLE)
10923 if (options.network)
10924 SendToServer_StopPlaying();
10928 game_status = GAME_MODE_MAIN;
10934 if (tape.playing && tape.deactivate_display)
10935 TapeDeactivateDisplayOff(TRUE);
10937 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
10939 if (tape.playing && tape.deactivate_display)
10940 TapeDeactivateDisplayOn();
10945 /* ---------- new game button stuff ---------------------------------------- */
10947 /* graphic position values for game buttons */
10948 #define GAME_BUTTON_XSIZE 30
10949 #define GAME_BUTTON_YSIZE 30
10950 #define GAME_BUTTON_XPOS 5
10951 #define GAME_BUTTON_YPOS 215
10952 #define SOUND_BUTTON_XPOS 5
10953 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
10955 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10956 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10957 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10958 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10959 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10960 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10967 } gamebutton_info[NUM_GAME_BUTTONS] =
10970 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
10975 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
10976 GAME_CTRL_ID_PAUSE,
10980 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
10985 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
10986 SOUND_CTRL_ID_MUSIC,
10987 "background music on/off"
10990 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
10991 SOUND_CTRL_ID_LOOPS,
10992 "sound loops on/off"
10995 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
10996 SOUND_CTRL_ID_SIMPLE,
10997 "normal sounds on/off"
11001 void CreateGameButtons()
11005 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11007 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
11008 struct GadgetInfo *gi;
11011 unsigned long event_mask;
11012 int gd_xoffset, gd_yoffset;
11013 int gd_x1, gd_x2, gd_y1, gd_y2;
11016 gd_xoffset = gamebutton_info[i].x;
11017 gd_yoffset = gamebutton_info[i].y;
11018 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
11019 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
11021 if (id == GAME_CTRL_ID_STOP ||
11022 id == GAME_CTRL_ID_PAUSE ||
11023 id == GAME_CTRL_ID_PLAY)
11025 button_type = GD_TYPE_NORMAL_BUTTON;
11027 event_mask = GD_EVENT_RELEASED;
11028 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11029 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11033 button_type = GD_TYPE_CHECK_BUTTON;
11035 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
11036 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
11037 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
11038 event_mask = GD_EVENT_PRESSED;
11039 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
11040 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11043 gi = CreateGadget(GDI_CUSTOM_ID, id,
11044 GDI_INFO_TEXT, gamebutton_info[i].infotext,
11045 GDI_X, DX + gd_xoffset,
11046 GDI_Y, DY + gd_yoffset,
11047 GDI_WIDTH, GAME_BUTTON_XSIZE,
11048 GDI_HEIGHT, GAME_BUTTON_YSIZE,
11049 GDI_TYPE, button_type,
11050 GDI_STATE, GD_BUTTON_UNPRESSED,
11051 GDI_CHECKED, checked,
11052 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
11053 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
11054 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
11055 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
11056 GDI_EVENT_MASK, event_mask,
11057 GDI_CALLBACK_ACTION, HandleGameButtons,
11061 Error(ERR_EXIT, "cannot create gadget");
11063 game_gadget[id] = gi;
11067 void FreeGameButtons()
11071 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11072 FreeGadget(game_gadget[i]);
11075 static void MapGameButtons()
11079 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11080 MapGadget(game_gadget[i]);
11083 void UnmapGameButtons()
11087 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11088 UnmapGadget(game_gadget[i]);
11091 static void HandleGameButtons(struct GadgetInfo *gi)
11093 int id = gi->custom_id;
11095 if (game_status != GAME_MODE_PLAYING)
11100 case GAME_CTRL_ID_STOP:
11101 RequestQuitGame(TRUE);
11104 case GAME_CTRL_ID_PAUSE:
11105 if (options.network)
11107 #if defined(NETWORK_AVALIABLE)
11109 SendToServer_ContinuePlaying();
11111 SendToServer_PausePlaying();
11115 TapeTogglePause(TAPE_TOGGLE_MANUAL);
11118 case GAME_CTRL_ID_PLAY:
11121 #if defined(NETWORK_AVALIABLE)
11122 if (options.network)
11123 SendToServer_ContinuePlaying();
11127 tape.pausing = FALSE;
11128 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
11133 case SOUND_CTRL_ID_MUSIC:
11134 if (setup.sound_music)
11136 setup.sound_music = FALSE;
11139 else if (audio.music_available)
11141 setup.sound = setup.sound_music = TRUE;
11143 SetAudioMode(setup.sound);
11149 case SOUND_CTRL_ID_LOOPS:
11150 if (setup.sound_loops)
11151 setup.sound_loops = FALSE;
11152 else if (audio.loops_available)
11154 setup.sound = setup.sound_loops = TRUE;
11155 SetAudioMode(setup.sound);
11159 case SOUND_CTRL_ID_SIMPLE:
11160 if (setup.sound_simple)
11161 setup.sound_simple = FALSE;
11162 else if (audio.sound_available)
11164 setup.sound = setup.sound_simple = TRUE;
11165 SetAudioMode(setup.sound);