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 /* this switch controls how rocks move horizontally */
25 #define OLD_GAME_BEHAVIOUR FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_AMOEBA_CODE FALSE
35 /* for MovePlayer() */
36 #define MF_NO_ACTION 0
40 /* for ScrollPlayer() */
42 #define SCROLL_GO_ON 1
45 #define EX_PHASE_START 0
46 #define EX_NO_EXPLOSION 0
51 /* special positions in the game control window (relative to control window) */
54 #define XX_EMERALDS 29
55 #define YY_EMERALDS 54
56 #define XX_DYNAMITE 29
57 #define YY_DYNAMITE 89
66 /* special positions in the game control window (relative to main window) */
67 #define DX_LEVEL (DX + XX_LEVEL)
68 #define DY_LEVEL (DY + YY_LEVEL)
69 #define DX_EMERALDS (DX + XX_EMERALDS)
70 #define DY_EMERALDS (DY + YY_EMERALDS)
71 #define DX_DYNAMITE (DX + XX_DYNAMITE)
72 #define DY_DYNAMITE (DY + YY_DYNAMITE)
73 #define DX_KEYS (DX + XX_KEYS)
74 #define DY_KEYS (DY + YY_KEYS)
75 #define DX_SCORE (DX + XX_SCORE)
76 #define DY_SCORE (DY + YY_SCORE)
77 #define DX_TIME1 (DX + XX_TIME1)
78 #define DX_TIME2 (DX + XX_TIME2)
79 #define DY_TIME (DY + YY_TIME)
81 /* values for initial player move delay (initial delay counter value) */
82 #define INITIAL_MOVE_DELAY_OFF -1
83 #define INITIAL_MOVE_DELAY_ON 0
85 /* values for player movement speed (which is in fact a delay value) */
86 #define MOVE_DELAY_NORMAL_SPEED 8
87 #define MOVE_DELAY_HIGH_SPEED 4
89 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
90 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
91 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
92 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
94 /* values for other actions */
95 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
97 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
99 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
100 RND(element_info[e].push_delay_random))
101 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
102 RND(element_info[e].move_delay_random))
103 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
104 (element_info[e].move_delay_random))
106 #define ELEMENT_CAN_ENTER_FIELD_BASE(e, x, y, condition) \
107 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
108 (CAN_MOVE_INTO_ACID(e) && \
109 Feld[x][y] == EL_ACID) || \
113 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
114 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
116 (DONT_COLLIDE_WITH(e) && \
118 !PLAYER_ENEMY_PROTECTED(x, y))))
120 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
121 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
123 (CAN_MOVE_INTO_ACID(e) && \
124 Feld[x][y] == EL_ACID) || \
125 (DONT_COLLIDE_WITH(e) && \
127 !PLAYER_ENEMY_PROTECTED(x, y))))
130 #define ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, condition) \
131 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
134 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
135 ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, 0)
137 #define ELEMENT_CAN_ENTER_FIELD_OR_ACID(e, x, y) \
138 ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, (Feld[x][y] == EL_ACID))
140 #define ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(x, y) \
141 ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, (Feld[x][y] == EL_ACID))
144 #define ENEMY_CAN_ENTER_FIELD(e, x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
146 #define ENEMY_CAN_ENTER_FIELD(e, x, y) ELEMENT_CAN_ENTER_FIELD_BASE(e, x, y, 0)
149 #define YAMYAM_CAN_ENTER_FIELD(x, y) \
150 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
151 (CAN_MOVE_INTO_ACID(EL_YAMYAM) && \
152 Feld[x][y] == EL_ACID) || \
153 Feld[x][y] == EL_DIAMOND))
155 #define DARK_YAMYAM_CAN_ENTER_FIELD(x, y) \
156 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
157 (CAN_MOVE_INTO_ACID(EL_DARK_YAMYAM) &&\
158 Feld[x][y] == EL_ACID) || \
159 IS_FOOD_DARK_YAMYAM(Feld[x][y])))
161 #define PACMAN_CAN_ENTER_FIELD(x, y) \
162 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
163 (CAN_MOVE_INTO_ACID(EL_PACMAN) && \
164 Feld[x][y] == EL_ACID) || \
165 IS_AMOEBOID(Feld[x][y])))
167 #define PIG_CAN_ENTER_FIELD(x, y) \
168 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
169 (CAN_MOVE_INTO_ACID(EL_PIG) && \
170 Feld[x][y] == EL_ACID) || \
171 IS_FOOD_PIG(Feld[x][y])))
173 #define PENGUIN_CAN_ENTER_FIELD(x, y) \
174 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
175 (CAN_MOVE_INTO_ACID(EL_PENGUIN) && \
176 Feld[x][y] == EL_ACID) || \
177 IS_FOOD_PENGUIN(Feld[x][y]) || \
178 Feld[x][y] == EL_EXIT_OPEN))
180 #define DRAGON_CAN_ENTER_FIELD(x, y) \
181 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
182 (CAN_MOVE_INTO_ACID(EL_DRAGON) && \
183 Feld[x][y] == EL_ACID)))
185 #define MOLE_CAN_ENTER_FIELD(x, y, condition) \
186 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
187 (CAN_MOVE_INTO_ACID(EL_MOLE) && \
188 Feld[x][y] == EL_ACID) || \
191 #define SPRING_CAN_ENTER_FIELD(x, y) \
192 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
193 (CAN_MOVE_INTO_ACID(EL_SPRING) && \
194 Feld[x][y] == EL_ACID)))
196 #define GROUP_NR(e) ((e) - EL_GROUP_START)
197 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
198 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
199 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
201 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
202 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
205 #define CE_ENTER_FIELD_COND(e, x, y) \
206 (!IS_PLAYER(x, y) && \
207 (Feld[x][y] == EL_ACID || \
208 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e))))
210 #define CE_ENTER_FIELD_COND(e, x, y) \
211 (!IS_PLAYER(x, y) && \
212 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
215 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
216 ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
218 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
219 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
221 /* game button identifiers */
222 #define GAME_CTRL_ID_STOP 0
223 #define GAME_CTRL_ID_PAUSE 1
224 #define GAME_CTRL_ID_PLAY 2
225 #define SOUND_CTRL_ID_MUSIC 3
226 #define SOUND_CTRL_ID_LOOPS 4
227 #define SOUND_CTRL_ID_SIMPLE 5
229 #define NUM_GAME_BUTTONS 6
232 /* forward declaration for internal use */
234 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
235 static boolean MovePlayer(struct PlayerInfo *, int, int);
236 static void ScrollPlayer(struct PlayerInfo *, int);
237 static void ScrollScreen(struct PlayerInfo *, int);
239 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
241 static void InitBeltMovement(void);
242 static void CloseAllOpenTimegates(void);
243 static void CheckGravityMovement(struct PlayerInfo *);
244 static void KillHeroUnlessEnemyProtected(int, int);
245 static void KillHeroUnlessExplosionProtected(int, int);
247 static void TestIfPlayerTouchesCustomElement(int, int);
248 static void TestIfElementTouchesCustomElement(int, int);
249 static void TestIfElementHitsCustomElement(int, int, int);
251 static void ChangeElement(int, int, int);
253 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
254 #define CheckTriggeredElementChange(x, y, e, ev) \
255 CheckTriggeredElementChangeExt(x, y, e, ev, -1, CH_SIDE_ANY, -1)
256 #define CheckTriggeredElementChangePlayer(x, y, e, ev, p, s) \
257 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
258 #define CheckTriggeredElementChangeSide(x, y, e, ev, s) \
259 CheckTriggeredElementChangeExt(x, y, e, ev, -1, s, -1)
260 #define CheckTriggeredElementChangePage(x, y, e, ev, p) \
261 CheckTriggeredElementChangeExt(x, y, e, ev, -1, CH_SIDE_ANY, p)
263 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
264 #define CheckElementChange(x, y, e, ev) \
265 CheckElementChangeExt(x, y, e, ev, -1, CH_SIDE_ANY, -1)
266 #define CheckElementChangePlayer(x, y, e, ev, p, s) \
267 CheckElementChangeExt(x, y, e, ev, p, s, -1)
268 #define CheckElementChangeSide(x, y, e, ev, s) \
269 CheckElementChangeExt(x, y, e, ev, -1, s, -1)
270 #define CheckElementChangePage(x, y, e, ev, p) \
271 CheckElementChangeExt(x, y, e, ev, -1, CH_SIDE_ANY, p)
273 static void PlayLevelSound(int, int, int);
274 static void PlayLevelSoundNearest(int, int, int);
275 static void PlayLevelSoundAction(int, int, int);
276 static void PlayLevelSoundElementAction(int, int, int, int);
277 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
278 static void PlayLevelSoundActionIfLoop(int, int, int);
279 static void StopLevelSoundActionIfLoop(int, int, int);
280 static void PlayLevelMusic();
282 static void MapGameButtons();
283 static void HandleGameButtons(struct GadgetInfo *);
285 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
288 /* ------------------------------------------------------------------------- */
289 /* definition of elements that automatically change to other elements after */
290 /* a specified time, eventually calling a function when changing */
291 /* ------------------------------------------------------------------------- */
293 /* forward declaration for changer functions */
294 static void InitBuggyBase(int x, int y);
295 static void WarnBuggyBase(int x, int y);
297 static void InitTrap(int x, int y);
298 static void ActivateTrap(int x, int y);
299 static void ChangeActiveTrap(int x, int y);
301 static void InitRobotWheel(int x, int y);
302 static void RunRobotWheel(int x, int y);
303 static void StopRobotWheel(int x, int y);
305 static void InitTimegateWheel(int x, int y);
306 static void RunTimegateWheel(int x, int y);
308 struct ChangingElementInfo
313 void (*pre_change_function)(int x, int y);
314 void (*change_function)(int x, int y);
315 void (*post_change_function)(int x, int y);
318 static struct ChangingElementInfo change_delay_list[] =
369 EL_SWITCHGATE_OPENING,
377 EL_SWITCHGATE_CLOSING,
378 EL_SWITCHGATE_CLOSED,
410 EL_ACID_SPLASH_RIGHT,
419 EL_SP_BUGGY_BASE_ACTIVATING,
426 EL_SP_BUGGY_BASE_ACTIVATING,
427 EL_SP_BUGGY_BASE_ACTIVE,
434 EL_SP_BUGGY_BASE_ACTIVE,
458 EL_ROBOT_WHEEL_ACTIVE,
466 EL_TIMEGATE_SWITCH_ACTIVE,
487 int push_delay_fixed, push_delay_random;
492 { EL_BALLOON, 0, 0 },
494 { EL_SOKOBAN_OBJECT, 2, 0 },
495 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
496 { EL_SATELLITE, 2, 0 },
497 { EL_SP_DISK_YELLOW, 2, 0 },
499 { EL_UNDEFINED, 0, 0 },
507 move_stepsize_list[] =
509 { EL_AMOEBA_DROP, 2 },
510 { EL_AMOEBA_DROPPING, 2 },
511 { EL_QUICKSAND_FILLING, 1 },
512 { EL_QUICKSAND_EMPTYING, 1 },
513 { EL_MAGIC_WALL_FILLING, 2 },
514 { EL_BD_MAGIC_WALL_FILLING, 2 },
515 { EL_MAGIC_WALL_EMPTYING, 2 },
516 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
526 collect_count_list[] =
529 { EL_BD_DIAMOND, 1 },
530 { EL_EMERALD_YELLOW, 1 },
531 { EL_EMERALD_RED, 1 },
532 { EL_EMERALD_PURPLE, 1 },
534 { EL_SP_INFOTRON, 1 },
548 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
549 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
550 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
551 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
552 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
553 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
554 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
555 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
556 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
557 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
558 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
563 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
565 #define IS_AUTO_CHANGING(e) (element_info[e].change_events & \
566 CH_EVENT_BIT(CE_DELAY))
567 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
568 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
569 IS_JUST_CHANGING(x, y))
571 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
574 void GetPlayerConfig()
576 if (!audio.sound_available)
577 setup.sound_simple = FALSE;
579 if (!audio.loops_available)
580 setup.sound_loops = FALSE;
582 if (!audio.music_available)
583 setup.sound_music = FALSE;
585 if (!video.fullscreen_available)
586 setup.fullscreen = FALSE;
588 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
590 SetAudioMode(setup.sound);
594 static int getBeltNrFromBeltElement(int element)
596 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
597 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
598 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
601 static int getBeltNrFromBeltActiveElement(int element)
603 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
604 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
605 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
608 static int getBeltNrFromBeltSwitchElement(int element)
610 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
611 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
612 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
615 static int getBeltDirNrFromBeltSwitchElement(int element)
617 static int belt_base_element[4] =
619 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
620 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
621 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
622 EL_CONVEYOR_BELT_4_SWITCH_LEFT
625 int belt_nr = getBeltNrFromBeltSwitchElement(element);
626 int belt_dir_nr = element - belt_base_element[belt_nr];
628 return (belt_dir_nr % 3);
631 static int getBeltDirFromBeltSwitchElement(int element)
633 static int belt_move_dir[3] =
640 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
642 return belt_move_dir[belt_dir_nr];
645 static void InitPlayerField(int x, int y, int element, boolean init_game)
647 if (element == EL_SP_MURPHY)
651 if (stored_player[0].present)
653 Feld[x][y] = EL_SP_MURPHY_CLONE;
659 stored_player[0].use_murphy_graphic = TRUE;
662 Feld[x][y] = EL_PLAYER_1;
668 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
669 int jx = player->jx, jy = player->jy;
671 player->present = TRUE;
673 player->block_last_field = (element == EL_SP_MURPHY ?
674 level.sp_block_last_field :
675 level.block_last_field);
677 if (!options.network || player->connected)
679 player->active = TRUE;
681 /* remove potentially duplicate players */
682 if (StorePlayer[jx][jy] == Feld[x][y])
683 StorePlayer[jx][jy] = 0;
685 StorePlayer[x][y] = Feld[x][y];
689 printf("Player %d activated.\n", player->element_nr);
690 printf("[Local player is %d and currently %s.]\n",
691 local_player->element_nr,
692 local_player->active ? "active" : "not active");
696 Feld[x][y] = EL_EMPTY;
697 player->jx = player->last_jx = x;
698 player->jy = player->last_jy = y;
702 static void InitField(int x, int y, boolean init_game)
704 int element = Feld[x][y];
713 InitPlayerField(x, y, element, init_game);
716 case EL_SOKOBAN_FIELD_PLAYER:
717 element = Feld[x][y] = EL_PLAYER_1;
718 InitField(x, y, init_game);
720 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
721 InitField(x, y, init_game);
724 case EL_SOKOBAN_FIELD_EMPTY:
725 local_player->sokobanfields_still_needed++;
729 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
730 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
731 else if (x > 0 && Feld[x-1][y] == EL_ACID)
732 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
733 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
734 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
735 else if (y > 0 && Feld[x][y-1] == EL_ACID)
736 Feld[x][y] = EL_ACID_POOL_BOTTOM;
737 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
738 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
746 case EL_SPACESHIP_RIGHT:
747 case EL_SPACESHIP_UP:
748 case EL_SPACESHIP_LEFT:
749 case EL_SPACESHIP_DOWN:
751 case EL_BD_BUTTERFLY_RIGHT:
752 case EL_BD_BUTTERFLY_UP:
753 case EL_BD_BUTTERFLY_LEFT:
754 case EL_BD_BUTTERFLY_DOWN:
755 case EL_BD_BUTTERFLY:
756 case EL_BD_FIREFLY_RIGHT:
757 case EL_BD_FIREFLY_UP:
758 case EL_BD_FIREFLY_LEFT:
759 case EL_BD_FIREFLY_DOWN:
761 case EL_PACMAN_RIGHT:
785 if (y == lev_fieldy - 1)
787 Feld[x][y] = EL_AMOEBA_GROWING;
788 Store[x][y] = EL_AMOEBA_WET;
792 case EL_DYNAMITE_ACTIVE:
793 case EL_SP_DISK_RED_ACTIVE:
794 case EL_DYNABOMB_PLAYER_1_ACTIVE:
795 case EL_DYNABOMB_PLAYER_2_ACTIVE:
796 case EL_DYNABOMB_PLAYER_3_ACTIVE:
797 case EL_DYNABOMB_PLAYER_4_ACTIVE:
802 local_player->lights_still_needed++;
806 local_player->friends_still_needed++;
811 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
816 Feld[x][y] = EL_EMPTY;
821 case EL_EM_KEY_1_FILE:
822 Feld[x][y] = EL_EM_KEY_1;
824 case EL_EM_KEY_2_FILE:
825 Feld[x][y] = EL_EM_KEY_2;
827 case EL_EM_KEY_3_FILE:
828 Feld[x][y] = EL_EM_KEY_3;
830 case EL_EM_KEY_4_FILE:
831 Feld[x][y] = EL_EM_KEY_4;
835 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
836 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
837 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
838 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
839 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
840 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
841 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
842 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
843 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
844 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
845 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
846 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
849 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
850 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
851 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
853 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
855 game.belt_dir[belt_nr] = belt_dir;
856 game.belt_dir_nr[belt_nr] = belt_dir_nr;
858 else /* more than one switch -- set it like the first switch */
860 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
865 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
867 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
870 case EL_LIGHT_SWITCH_ACTIVE:
872 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
876 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
878 else if (IS_GROUP_ELEMENT(element))
880 struct ElementGroupInfo *group = element_info[element].group;
881 int last_anim_random_frame = gfx.anim_random_frame;
884 if (group->choice_mode == ANIM_RANDOM)
885 gfx.anim_random_frame = RND(group->num_elements_resolved);
887 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
888 group->choice_mode, 0,
891 if (group->choice_mode == ANIM_RANDOM)
892 gfx.anim_random_frame = last_anim_random_frame;
896 Feld[x][y] = group->element_resolved[element_pos];
898 InitField(x, y, init_game);
904 static inline void InitField_WithBug1(int x, int y, boolean init_game)
906 InitField(x, y, init_game);
908 /* not needed to call InitMovDir() -- already done by InitField()! */
909 if (game.engine_version < VERSION_IDENT(3,0,9,0) &&
910 CAN_MOVE(Feld[x][y]))
914 static inline void InitField_WithBug2(int x, int y, boolean init_game)
916 int old_element = Feld[x][y];
918 InitField(x, y, init_game);
920 /* not needed to call InitMovDir() -- already done by InitField()! */
921 if (game.engine_version < VERSION_IDENT(3,0,9,0) &&
922 CAN_MOVE(old_element) &&
923 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
926 /* this case is in fact a combination of not less than three bugs:
927 first, it calls InitMovDir() for elements that can move, although this is
928 already done by InitField(); then, it checks the element that was at this
929 field _before_ the call to InitField() (which can change it)
934 inline void DrawGameValue_Emeralds(int value)
936 DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
939 inline void DrawGameValue_Dynamite(int value)
941 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
944 inline void DrawGameValue_Keys(struct PlayerInfo *player)
948 for (i = 0; i < MAX_KEYS; i++)
950 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
951 el2edimg(EL_KEY_1 + i));
954 inline void DrawGameValue_Score(int value)
956 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
959 inline void DrawGameValue_Time(int value)
962 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
964 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
967 inline void DrawGameValue_Level(int value)
970 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
973 /* misuse area for displaying emeralds to draw bigger level number */
974 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
975 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
977 /* now copy it to the area for displaying level number */
978 BlitBitmap(drawto, drawto,
979 DX_EMERALDS, DY_EMERALDS + 1,
980 getFontWidth(FONT_LEVEL_NUMBER) * 3,
981 getFontHeight(FONT_LEVEL_NUMBER) - 1,
982 DX_LEVEL - 1, DY_LEVEL + 1);
984 /* restore the area for displaying emeralds */
985 DrawGameValue_Emeralds(local_player->gems_still_needed);
987 /* yes, this is all really ugly :-) */
991 void DrawGameDoorValues()
995 DrawGameValue_Level(level_nr);
997 for (i = 0; i < MAX_PLAYERS; i++)
998 DrawGameValue_Keys(&stored_player[i]);
1000 DrawGameValue_Emeralds(local_player->gems_still_needed);
1001 DrawGameValue_Dynamite(local_player->inventory_size);
1002 DrawGameValue_Score(local_player->score);
1003 DrawGameValue_Time(TimeLeft);
1006 static void resolve_group_element(int group_element, int recursion_depth)
1008 static int group_nr;
1009 static struct ElementGroupInfo *group;
1010 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1013 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1015 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1016 group_element - EL_GROUP_START + 1);
1018 /* replace element which caused too deep recursion by question mark */
1019 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1024 if (recursion_depth == 0) /* initialization */
1026 group = element_info[group_element].group;
1027 group_nr = group_element - EL_GROUP_START;
1029 group->num_elements_resolved = 0;
1030 group->choice_pos = 0;
1033 for (i = 0; i < actual_group->num_elements; i++)
1035 int element = actual_group->element[i];
1037 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1040 if (IS_GROUP_ELEMENT(element))
1041 resolve_group_element(element, recursion_depth + 1);
1044 group->element_resolved[group->num_elements_resolved++] = element;
1045 element_info[element].in_group[group_nr] = TRUE;
1050 if (recursion_depth == 0 && group_element <= EL_GROUP_4)
1052 printf("::: group %d: %d resolved elements\n",
1053 group_element - EL_GROUP_START, group->num_elements_resolved);
1054 for (i = 0; i < group->num_elements_resolved; i++)
1055 printf("::: - %d ['%s']\n", group->element_resolved[i],
1056 element_info[group->element_resolved[i]].token_name);
1063 =============================================================================
1065 -----------------------------------------------------------------------------
1066 initialize game engine due to level / tape version number
1067 =============================================================================
1070 static void InitGameEngine()
1074 /* set game engine from tape file when re-playing, else from level file */
1075 game.engine_version = (tape.playing ? tape.engine_version :
1076 level.game_version);
1078 /* dynamically adjust element properties according to game engine version */
1079 InitElementPropertiesEngine(game.engine_version);
1082 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1083 printf(" tape version == %06d [%s] [file: %06d]\n",
1084 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1086 printf(" => game.engine_version == %06d\n", game.engine_version);
1089 /* ---------- recursively resolve group elements ------------------------- */
1091 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1092 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1093 element_info[i].in_group[j] = FALSE;
1095 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1096 resolve_group_element(EL_GROUP_START + i, 0);
1098 /* ---------- initialize player's initial move delay --------------------- */
1100 /* dynamically adjust player properties according to game engine version */
1101 game.initial_move_delay =
1102 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
1103 INITIAL_MOVE_DELAY_OFF);
1105 /* dynamically adjust player properties according to level information */
1106 game.initial_move_delay_value =
1107 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1109 /* ---------- initialize player's initial push delay --------------------- */
1111 /* dynamically adjust player properties according to game engine version */
1112 game.initial_push_delay_value =
1113 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1115 /* ---------- initialize changing elements ------------------------------- */
1117 /* initialize changing elements information */
1118 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1120 struct ElementInfo *ei = &element_info[i];
1122 /* this pointer might have been changed in the level editor */
1123 ei->change = &ei->change_page[0];
1125 if (!IS_CUSTOM_ELEMENT(i))
1127 ei->change->target_element = EL_EMPTY_SPACE;
1128 ei->change->delay_fixed = 0;
1129 ei->change->delay_random = 0;
1130 ei->change->delay_frames = 1;
1133 ei->change_events = CE_BITMASK_DEFAULT;
1134 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1136 ei->event_page_nr[j] = 0;
1137 ei->event_page[j] = &ei->change_page[0];
1141 /* add changing elements from pre-defined list */
1142 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1144 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1145 struct ElementInfo *ei = &element_info[ch_delay->element];
1147 ei->change->target_element = ch_delay->target_element;
1148 ei->change->delay_fixed = ch_delay->change_delay;
1150 ei->change->pre_change_function = ch_delay->pre_change_function;
1151 ei->change->change_function = ch_delay->change_function;
1152 ei->change->post_change_function = ch_delay->post_change_function;
1154 ei->change_events |= CH_EVENT_BIT(CE_DELAY);
1158 /* add change events from custom element configuration */
1159 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1161 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1163 for (j = 0; j < ei->num_change_pages; j++)
1165 if (!ei->change_page[j].can_change)
1168 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1170 /* only add event page for the first page found with this event */
1171 if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
1172 !(ei->change_events & CH_EVENT_BIT(k)))
1174 ei->change_events |= CH_EVENT_BIT(k);
1175 ei->event_page_nr[k] = j;
1176 ei->event_page[k] = &ei->change_page[j];
1184 /* add change events from custom element configuration */
1185 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1187 int element = EL_CUSTOM_START + i;
1189 /* only add custom elements that change after fixed/random frame delay */
1190 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
1191 element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
1195 /* ---------- initialize trigger events ---------------------------------- */
1197 /* initialize trigger events information */
1198 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1199 trigger_events[i] = EP_BITMASK_DEFAULT;
1202 /* add trigger events from element change event properties */
1203 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1205 struct ElementInfo *ei = &element_info[i];
1207 for (j = 0; j < ei->num_change_pages; j++)
1209 if (!ei->change_page[j].can_change)
1212 if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
1214 int trigger_element = ei->change_page[j].trigger_element;
1216 if (IS_GROUP_ELEMENT(trigger_element))
1218 struct ElementGroupInfo *group = element_info[trigger_element].group;
1220 for (k = 0; k < group->num_elements_resolved; k++)
1221 trigger_events[group->element_resolved[k]]
1222 |= ei->change_page[j].events;
1225 trigger_events[trigger_element] |= ei->change_page[j].events;
1230 /* add trigger events from element change event properties */
1231 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1232 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
1233 trigger_events[element_info[i].change->trigger_element] |=
1234 element_info[i].change->events;
1237 /* ---------- initialize push delay -------------------------------------- */
1239 /* initialize push delay values to default */
1240 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1242 if (!IS_CUSTOM_ELEMENT(i))
1244 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1245 element_info[i].push_delay_random = game.default_push_delay_random;
1249 /* set push delay value for certain elements from pre-defined list */
1250 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1252 int e = push_delay_list[i].element;
1254 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1255 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1258 /* set push delay value for Supaplex elements for newer engine versions */
1259 if (game.engine_version >= VERSION_IDENT(3,0,9,0))
1261 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1263 if (IS_SP_ELEMENT(i))
1265 element_info[i].push_delay_fixed = 6;
1266 element_info[i].push_delay_random = 0;
1271 /* ---------- initialize move stepsize ----------------------------------- */
1273 /* initialize move stepsize values to default */
1274 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1275 if (!IS_CUSTOM_ELEMENT(i))
1276 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1278 /* set move stepsize value for certain elements from pre-defined list */
1279 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1281 int e = move_stepsize_list[i].element;
1283 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1286 /* ---------- initialize move dig/leave ---------------------------------- */
1288 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1290 element_info[i].can_leave_element = FALSE;
1291 element_info[i].can_leave_element_last = FALSE;
1294 /* ---------- initialize gem count --------------------------------------- */
1296 /* initialize gem count values for each element */
1297 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1298 if (!IS_CUSTOM_ELEMENT(i))
1299 element_info[i].collect_count = 0;
1301 /* add gem count values for all elements from pre-defined list */
1302 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1303 element_info[collect_count_list[i].element].collect_count =
1304 collect_count_list[i].count;
1306 /* ---------- initialize access direction -------------------------------- */
1308 /* initialize access direction values to default */
1309 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1310 if (!IS_CUSTOM_ELEMENT(i))
1311 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1313 /* set access direction value for certain elements from pre-defined list */
1314 for (i = 0; tube_access[i].element != EL_UNDEFINED; i++)
1315 element_info[tube_access[i].element].access_direction =
1316 tube_access[i].direction;
1321 =============================================================================
1323 -----------------------------------------------------------------------------
1324 initialize and start new game
1325 =============================================================================
1330 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1331 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1332 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1339 #if USE_NEW_AMOEBA_CODE
1340 printf("Using new amoeba code.\n");
1342 printf("Using old amoeba code.\n");
1347 /* don't play tapes over network */
1348 network_playing = (options.network && !tape.playing);
1350 for (i = 0; i < MAX_PLAYERS; i++)
1352 struct PlayerInfo *player = &stored_player[i];
1354 player->index_nr = i;
1355 player->index_bit = (1 << i);
1356 player->element_nr = EL_PLAYER_1 + i;
1358 player->present = FALSE;
1359 player->active = FALSE;
1362 player->effective_action = 0;
1363 player->programmed_action = 0;
1366 player->gems_still_needed = level.gems_needed;
1367 player->sokobanfields_still_needed = 0;
1368 player->lights_still_needed = 0;
1369 player->friends_still_needed = 0;
1371 for (j = 0; j < MAX_KEYS; j++)
1372 player->key[j] = FALSE;
1374 player->dynabomb_count = 0;
1375 player->dynabomb_size = 1;
1376 player->dynabombs_left = 0;
1377 player->dynabomb_xl = FALSE;
1379 player->MovDir = MV_NO_MOVING;
1382 player->GfxDir = MV_NO_MOVING;
1383 player->GfxAction = ACTION_DEFAULT;
1385 player->StepFrame = 0;
1387 player->use_murphy_graphic = FALSE;
1389 player->block_last_field = FALSE;
1391 player->actual_frame_counter = 0;
1393 player->step_counter = 0;
1395 player->last_move_dir = MV_NO_MOVING;
1397 player->is_waiting = FALSE;
1398 player->is_moving = FALSE;
1399 player->is_digging = FALSE;
1400 player->is_snapping = FALSE;
1401 player->is_collecting = FALSE;
1402 player->is_pushing = FALSE;
1403 player->is_switching = FALSE;
1404 player->is_dropping = FALSE;
1406 player->is_bored = FALSE;
1407 player->is_sleeping = FALSE;
1409 player->frame_counter_bored = -1;
1410 player->frame_counter_sleeping = -1;
1412 player->anim_delay_counter = 0;
1413 player->post_delay_counter = 0;
1415 player->action_waiting = ACTION_DEFAULT;
1416 player->last_action_waiting = ACTION_DEFAULT;
1417 player->special_action_bored = ACTION_DEFAULT;
1418 player->special_action_sleeping = ACTION_DEFAULT;
1420 player->num_special_action_bored = 0;
1421 player->num_special_action_sleeping = 0;
1423 /* determine number of special actions for bored and sleeping animation */
1424 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1426 boolean found = FALSE;
1428 for (k = 0; k < NUM_DIRECTIONS; k++)
1429 if (el_act_dir2img(player->element_nr, j, k) !=
1430 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1434 player->num_special_action_bored++;
1438 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1440 boolean found = FALSE;
1442 for (k = 0; k < NUM_DIRECTIONS; k++)
1443 if (el_act_dir2img(player->element_nr, j, k) !=
1444 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1448 player->num_special_action_sleeping++;
1453 player->switch_x = -1;
1454 player->switch_y = -1;
1456 player->show_envelope = 0;
1458 player->move_delay = game.initial_move_delay;
1459 player->move_delay_value = game.initial_move_delay_value;
1461 player->move_delay_reset_counter = 0;
1463 player->push_delay = 0;
1464 player->push_delay_value = game.initial_push_delay_value;
1466 player->drop_delay = 0;
1468 player->last_jx = player->last_jy = 0;
1469 player->jx = player->jy = 0;
1471 player->shield_normal_time_left = 0;
1472 player->shield_deadly_time_left = 0;
1474 player->inventory_size = 0;
1476 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1477 SnapField(player, 0, 0);
1479 player->LevelSolved = FALSE;
1480 player->GameOver = FALSE;
1483 network_player_action_received = FALSE;
1485 #if defined(PLATFORM_UNIX)
1486 /* initial null action */
1487 if (network_playing)
1488 SendToServer_MovePlayer(MV_NO_MOVING);
1496 TimeLeft = level.time;
1498 ScreenMovDir = MV_NO_MOVING;
1502 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1504 AllPlayersGone = FALSE;
1506 game.yamyam_content_nr = 0;
1507 game.magic_wall_active = FALSE;
1508 game.magic_wall_time_left = 0;
1509 game.light_time_left = 0;
1510 game.timegate_time_left = 0;
1511 game.switchgate_pos = 0;
1512 game.balloon_dir = MV_NO_MOVING;
1513 game.gravity = level.initial_gravity;
1514 game.explosions_delayed = TRUE;
1516 game.envelope_active = FALSE;
1518 for (i = 0; i < NUM_BELTS; i++)
1520 game.belt_dir[i] = MV_NO_MOVING;
1521 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1524 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1525 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1527 for (x = 0; x < lev_fieldx; x++)
1529 for (y = 0; y < lev_fieldy; y++)
1531 Feld[x][y] = level.field[x][y];
1532 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1533 ChangeDelay[x][y] = 0;
1534 ChangePage[x][y] = -1;
1535 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1537 WasJustMoving[x][y] = 0;
1538 WasJustFalling[x][y] = 0;
1540 Pushed[x][y] = FALSE;
1542 Changed[x][y] = CE_BITMASK_DEFAULT;
1543 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1545 ExplodePhase[x][y] = 0;
1546 ExplodeDelay[x][y] = 0;
1547 ExplodeField[x][y] = EX_NO_EXPLOSION;
1549 RunnerVisit[x][y] = 0;
1550 PlayerVisit[x][y] = 0;
1553 GfxRandom[x][y] = INIT_GFX_RANDOM();
1554 GfxElement[x][y] = EL_UNDEFINED;
1555 GfxAction[x][y] = ACTION_DEFAULT;
1556 GfxDir[x][y] = MV_NO_MOVING;
1560 for (y = 0; y < lev_fieldy; y++)
1562 for (x = 0; x < lev_fieldx; x++)
1564 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1566 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1568 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1571 InitField(x, y, TRUE);
1577 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1578 emulate_sb ? EMU_SOKOBAN :
1579 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1581 /* initialize explosion and ignition delay */
1582 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1584 if (!IS_CUSTOM_ELEMENT(i))
1587 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
1588 int last_phase = num_phase * delay;
1589 int half_phase = (num_phase / 2) * delay;
1591 element_info[i].explosion_delay = last_phase;
1592 element_info[i].ignition_delay = half_phase;
1594 if (i == EL_BLACK_ORB)
1595 element_info[i].ignition_delay = 1;
1598 if (element_info[i].explosion_delay < 2) /* !!! check again !!! */
1599 element_info[i].explosion_delay = 2;
1601 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
1602 element_info[i].ignition_delay = 1;
1605 /* correct non-moving belts to start moving left */
1606 for (i = 0; i < NUM_BELTS; i++)
1607 if (game.belt_dir[i] == MV_NO_MOVING)
1608 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1610 /* check if any connected player was not found in playfield */
1611 for (i = 0; i < MAX_PLAYERS; i++)
1613 struct PlayerInfo *player = &stored_player[i];
1615 if (player->connected && !player->present)
1617 for (j = 0; j < MAX_PLAYERS; j++)
1619 struct PlayerInfo *some_player = &stored_player[j];
1620 int jx = some_player->jx, jy = some_player->jy;
1622 /* assign first free player found that is present in the playfield */
1623 if (some_player->present && !some_player->connected)
1625 player->present = TRUE;
1626 player->active = TRUE;
1628 some_player->present = FALSE;
1629 some_player->active = FALSE;
1631 StorePlayer[jx][jy] = player->element_nr;
1632 player->jx = player->last_jx = jx;
1633 player->jy = player->last_jy = jy;
1643 /* when playing a tape, eliminate all players which do not participate */
1645 for (i = 0; i < MAX_PLAYERS; i++)
1647 if (stored_player[i].active && !tape.player_participates[i])
1649 struct PlayerInfo *player = &stored_player[i];
1650 int jx = player->jx, jy = player->jy;
1652 player->active = FALSE;
1653 StorePlayer[jx][jy] = 0;
1654 Feld[jx][jy] = EL_EMPTY;
1658 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1660 /* when in single player mode, eliminate all but the first active player */
1662 for (i = 0; i < MAX_PLAYERS; i++)
1664 if (stored_player[i].active)
1666 for (j = i + 1; j < MAX_PLAYERS; j++)
1668 if (stored_player[j].active)
1670 struct PlayerInfo *player = &stored_player[j];
1671 int jx = player->jx, jy = player->jy;
1673 player->active = FALSE;
1674 player->present = FALSE;
1676 StorePlayer[jx][jy] = 0;
1677 Feld[jx][jy] = EL_EMPTY;
1684 /* when recording the game, store which players take part in the game */
1687 for (i = 0; i < MAX_PLAYERS; i++)
1688 if (stored_player[i].active)
1689 tape.player_participates[i] = TRUE;
1694 for (i = 0; i < MAX_PLAYERS; i++)
1696 struct PlayerInfo *player = &stored_player[i];
1698 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1703 if (local_player == player)
1704 printf("Player %d is local player.\n", i+1);
1708 if (BorderElement == EL_EMPTY)
1711 SBX_Right = lev_fieldx - SCR_FIELDX;
1713 SBY_Lower = lev_fieldy - SCR_FIELDY;
1718 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1720 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1723 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1724 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1726 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1727 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1729 /* if local player not found, look for custom element that might create
1730 the player (make some assumptions about the right custom element) */
1731 if (!local_player->present)
1733 int start_x = 0, start_y = 0;
1734 int found_rating = 0;
1735 int found_element = EL_UNDEFINED;
1737 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1739 int element = Feld[x][y];
1744 if (!IS_CUSTOM_ELEMENT(element))
1747 if (CAN_CHANGE(element))
1749 for (i = 0; i < element_info[element].num_change_pages; i++)
1751 content = element_info[element].change_page[i].target_element;
1752 is_player = ELEM_IS_PLAYER(content);
1754 if (is_player && (found_rating < 3 || element < found_element))
1760 found_element = element;
1765 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1767 content = element_info[element].content[xx][yy];
1768 is_player = ELEM_IS_PLAYER(content);
1770 if (is_player && (found_rating < 2 || element < found_element))
1772 start_x = x + xx - 1;
1773 start_y = y + yy - 1;
1776 found_element = element;
1779 if (!CAN_CHANGE(element))
1782 for (i = 0; i < element_info[element].num_change_pages; i++)
1784 content = element_info[element].change_page[i].content[xx][yy];
1785 is_player = ELEM_IS_PLAYER(content);
1787 if (is_player && (found_rating < 1 || element < found_element))
1789 start_x = x + xx - 1;
1790 start_y = y + yy - 1;
1793 found_element = element;
1799 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1800 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1803 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1804 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1810 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1811 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1812 local_player->jx - MIDPOSX);
1814 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1815 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1816 local_player->jy - MIDPOSY);
1818 scroll_x = SBX_Left;
1819 scroll_y = SBY_Upper;
1820 if (local_player->jx >= SBX_Left + MIDPOSX)
1821 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1822 local_player->jx - MIDPOSX :
1824 if (local_player->jy >= SBY_Upper + MIDPOSY)
1825 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1826 local_player->jy - MIDPOSY :
1831 CloseDoor(DOOR_CLOSE_1);
1836 /* after drawing the level, correct some elements */
1837 if (game.timegate_time_left == 0)
1838 CloseAllOpenTimegates();
1840 if (setup.soft_scrolling)
1841 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1843 redraw_mask |= REDRAW_FROM_BACKBUFFER;
1846 /* copy default game door content to main double buffer */
1847 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1848 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1850 DrawGameDoorValues();
1854 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1855 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1856 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1860 /* copy actual game door content to door double buffer for OpenDoor() */
1861 BlitBitmap(drawto, bitmap_db_door,
1862 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1864 OpenDoor(DOOR_OPEN_ALL);
1866 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1868 if (setup.sound_music)
1871 KeyboardAutoRepeatOffUnlessAutoplay();
1875 for (i = 0; i < MAX_PLAYERS; i++)
1876 printf("Player %d %sactive.\n",
1877 i + 1, (stored_player[i].active ? "" : "not "));
1881 printf("::: starting game [%d]\n", FrameCounter);
1885 void InitMovDir(int x, int y)
1887 int i, element = Feld[x][y];
1888 static int xy[4][2] =
1895 static int direction[3][4] =
1897 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
1898 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
1899 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
1908 Feld[x][y] = EL_BUG;
1909 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
1912 case EL_SPACESHIP_RIGHT:
1913 case EL_SPACESHIP_UP:
1914 case EL_SPACESHIP_LEFT:
1915 case EL_SPACESHIP_DOWN:
1916 Feld[x][y] = EL_SPACESHIP;
1917 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
1920 case EL_BD_BUTTERFLY_RIGHT:
1921 case EL_BD_BUTTERFLY_UP:
1922 case EL_BD_BUTTERFLY_LEFT:
1923 case EL_BD_BUTTERFLY_DOWN:
1924 Feld[x][y] = EL_BD_BUTTERFLY;
1925 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
1928 case EL_BD_FIREFLY_RIGHT:
1929 case EL_BD_FIREFLY_UP:
1930 case EL_BD_FIREFLY_LEFT:
1931 case EL_BD_FIREFLY_DOWN:
1932 Feld[x][y] = EL_BD_FIREFLY;
1933 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
1936 case EL_PACMAN_RIGHT:
1938 case EL_PACMAN_LEFT:
1939 case EL_PACMAN_DOWN:
1940 Feld[x][y] = EL_PACMAN;
1941 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
1944 case EL_SP_SNIKSNAK:
1945 MovDir[x][y] = MV_UP;
1948 case EL_SP_ELECTRON:
1949 MovDir[x][y] = MV_LEFT;
1956 Feld[x][y] = EL_MOLE;
1957 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
1961 if (IS_CUSTOM_ELEMENT(element))
1963 struct ElementInfo *ei = &element_info[element];
1964 int move_direction_initial = ei->move_direction_initial;
1965 int move_pattern = ei->move_pattern;
1967 if (move_direction_initial == MV_START_PREVIOUS)
1969 if (MovDir[x][y] != MV_NO_MOVING)
1972 move_direction_initial = MV_START_AUTOMATIC;
1975 if (move_direction_initial == MV_START_RANDOM)
1976 MovDir[x][y] = 1 << RND(4);
1977 else if (move_direction_initial & MV_ANY_DIRECTION)
1978 MovDir[x][y] = move_direction_initial;
1979 else if (move_pattern == MV_ALL_DIRECTIONS ||
1980 move_pattern == MV_TURNING_LEFT ||
1981 move_pattern == MV_TURNING_RIGHT ||
1982 move_pattern == MV_TURNING_LEFT_RIGHT ||
1983 move_pattern == MV_TURNING_RIGHT_LEFT ||
1984 move_pattern == MV_TURNING_RANDOM)
1985 MovDir[x][y] = 1 << RND(4);
1986 else if (move_pattern == MV_HORIZONTAL)
1987 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
1988 else if (move_pattern == MV_VERTICAL)
1989 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
1990 else if (move_pattern & MV_ANY_DIRECTION)
1991 MovDir[x][y] = element_info[element].move_pattern;
1992 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
1993 move_pattern == MV_ALONG_RIGHT_SIDE)
1995 for (i = 0; i < NUM_DIRECTIONS; i++)
1997 int x1 = x + xy[i][0];
1998 int y1 = y + xy[i][1];
2000 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2002 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2003 MovDir[x][y] = direction[0][i];
2005 MovDir[x][y] = direction[1][i];
2014 MovDir[x][y] = 1 << RND(4);
2016 if (element != EL_BUG &&
2017 element != EL_SPACESHIP &&
2018 element != EL_BD_BUTTERFLY &&
2019 element != EL_BD_FIREFLY)
2022 for (i = 0; i < NUM_DIRECTIONS; i++)
2024 int x1 = x + xy[i][0];
2025 int y1 = y + xy[i][1];
2027 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2029 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2031 MovDir[x][y] = direction[0][i];
2034 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2035 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2037 MovDir[x][y] = direction[1][i];
2046 GfxDir[x][y] = MovDir[x][y];
2049 void InitAmoebaNr(int x, int y)
2052 int group_nr = AmoebeNachbarNr(x, y);
2056 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2058 if (AmoebaCnt[i] == 0)
2066 AmoebaNr[x][y] = group_nr;
2067 AmoebaCnt[group_nr]++;
2068 AmoebaCnt2[group_nr]++;
2074 boolean raise_level = FALSE;
2076 if (local_player->MovPos)
2080 if (tape.auto_play) /* tape might already be stopped here */
2081 tape.auto_play_level_solved = TRUE;
2083 if (tape.playing && tape.auto_play)
2084 tape.auto_play_level_solved = TRUE;
2087 local_player->LevelSolved = FALSE;
2089 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2093 if (!tape.playing && setup.sound_loops)
2094 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2095 SND_CTRL_PLAY_LOOP);
2097 while (TimeLeft > 0)
2099 if (!tape.playing && !setup.sound_loops)
2100 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2101 if (TimeLeft > 0 && !(TimeLeft % 10))
2102 RaiseScore(level.score[SC_TIME_BONUS]);
2103 if (TimeLeft > 100 && !(TimeLeft % 10))
2108 DrawGameValue_Time(TimeLeft);
2116 if (!tape.playing && setup.sound_loops)
2117 StopSound(SND_GAME_LEVELTIME_BONUS);
2119 else if (level.time == 0) /* level without time limit */
2121 if (!tape.playing && setup.sound_loops)
2122 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2123 SND_CTRL_PLAY_LOOP);
2125 while (TimePlayed < 999)
2127 if (!tape.playing && !setup.sound_loops)
2128 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2129 if (TimePlayed < 999 && !(TimePlayed % 10))
2130 RaiseScore(level.score[SC_TIME_BONUS]);
2131 if (TimePlayed < 900 && !(TimePlayed % 10))
2136 DrawGameValue_Time(TimePlayed);
2144 if (!tape.playing && setup.sound_loops)
2145 StopSound(SND_GAME_LEVELTIME_BONUS);
2148 /* close exit door after last player */
2149 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2150 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
2152 int element = Feld[ExitX][ExitY];
2154 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2155 EL_SP_EXIT_CLOSING);
2157 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2160 /* Hero disappears */
2161 DrawLevelField(ExitX, ExitY);
2167 CloseDoor(DOOR_CLOSE_1);
2172 SaveTape(tape.level_nr); /* Ask to save tape */
2175 if (level_nr == leveldir_current->handicap_level)
2177 leveldir_current->handicap_level++;
2178 SaveLevelSetup_SeriesInfo();
2181 if (level_editor_test_game)
2182 local_player->score = -1; /* no highscore when playing from editor */
2183 else if (level_nr < leveldir_current->last_level)
2184 raise_level = TRUE; /* advance to next level */
2186 if ((hi_pos = NewHiScore()) >= 0)
2188 game_status = GAME_MODE_SCORES;
2189 DrawHallOfFame(hi_pos);
2198 game_status = GAME_MODE_MAIN;
2215 LoadScore(level_nr);
2217 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2218 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2221 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2223 if (local_player->score > highscore[k].Score)
2225 /* player has made it to the hall of fame */
2227 if (k < MAX_SCORE_ENTRIES - 1)
2229 int m = MAX_SCORE_ENTRIES - 1;
2232 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2233 if (!strcmp(setup.player_name, highscore[l].Name))
2235 if (m == k) /* player's new highscore overwrites his old one */
2239 for (l = m; l > k; l--)
2241 strcpy(highscore[l].Name, highscore[l - 1].Name);
2242 highscore[l].Score = highscore[l - 1].Score;
2249 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2250 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2251 highscore[k].Score = local_player->score;
2257 else if (!strncmp(setup.player_name, highscore[k].Name,
2258 MAX_PLAYER_NAME_LEN))
2259 break; /* player already there with a higher score */
2265 SaveScore(level_nr);
2270 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2272 if (player->GfxAction != action || player->GfxDir != dir)
2275 printf("Player frame reset! (%d => %d, %d => %d)\n",
2276 player->GfxAction, action, player->GfxDir, dir);
2279 player->GfxAction = action;
2280 player->GfxDir = dir;
2282 player->StepFrame = 0;
2286 static void ResetRandomAnimationValue(int x, int y)
2288 GfxRandom[x][y] = INIT_GFX_RANDOM();
2291 static void ResetGfxAnimation(int x, int y)
2294 GfxAction[x][y] = ACTION_DEFAULT;
2295 GfxDir[x][y] = MovDir[x][y];
2298 void InitMovingField(int x, int y, int direction)
2300 int element = Feld[x][y];
2301 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2302 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2306 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2307 ResetGfxAnimation(x, y);
2309 MovDir[newx][newy] = MovDir[x][y] = direction;
2310 GfxDir[x][y] = direction;
2312 if (Feld[newx][newy] == EL_EMPTY)
2313 Feld[newx][newy] = EL_BLOCKED;
2315 if (direction == MV_DOWN && CAN_FALL(element))
2316 GfxAction[x][y] = ACTION_FALLING;
2318 GfxAction[x][y] = ACTION_MOVING;
2320 GfxFrame[newx][newy] = GfxFrame[x][y];
2321 GfxRandom[newx][newy] = GfxRandom[x][y];
2322 GfxAction[newx][newy] = GfxAction[x][y];
2323 GfxDir[newx][newy] = GfxDir[x][y];
2326 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2328 int direction = MovDir[x][y];
2329 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2330 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2336 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2338 int oldx = x, oldy = y;
2339 int direction = MovDir[x][y];
2341 if (direction == MV_LEFT)
2343 else if (direction == MV_RIGHT)
2345 else if (direction == MV_UP)
2347 else if (direction == MV_DOWN)
2350 *comes_from_x = oldx;
2351 *comes_from_y = oldy;
2354 int MovingOrBlocked2Element(int x, int y)
2356 int element = Feld[x][y];
2358 if (element == EL_BLOCKED)
2362 Blocked2Moving(x, y, &oldx, &oldy);
2363 return Feld[oldx][oldy];
2369 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2371 /* like MovingOrBlocked2Element(), but if element is moving
2372 and (x,y) is the field the moving element is just leaving,
2373 return EL_BLOCKED instead of the element value */
2374 int element = Feld[x][y];
2376 if (IS_MOVING(x, y))
2378 if (element == EL_BLOCKED)
2382 Blocked2Moving(x, y, &oldx, &oldy);
2383 return Feld[oldx][oldy];
2392 static void RemoveField(int x, int y)
2394 Feld[x][y] = EL_EMPTY;
2401 ChangeDelay[x][y] = 0;
2402 ChangePage[x][y] = -1;
2403 Pushed[x][y] = FALSE;
2405 GfxElement[x][y] = EL_UNDEFINED;
2406 GfxAction[x][y] = ACTION_DEFAULT;
2407 GfxDir[x][y] = MV_NO_MOVING;
2410 void RemoveMovingField(int x, int y)
2412 int oldx = x, oldy = y, newx = x, newy = y;
2413 int element = Feld[x][y];
2414 int next_element = EL_UNDEFINED;
2416 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2419 if (IS_MOVING(x, y))
2421 Moving2Blocked(x, y, &newx, &newy);
2423 if (Feld[newx][newy] != EL_BLOCKED)
2426 if (Feld[newx][newy] != EL_BLOCKED)
2428 /* element is moving, but target field is not free (blocked), but
2429 already occupied by something different (example: acid pool);
2430 in this case, only remove the moving field, but not the target */
2432 RemoveField(oldx, oldy);
2434 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2436 DrawLevelField(oldx, oldy);
2442 else if (element == EL_BLOCKED)
2444 Blocked2Moving(x, y, &oldx, &oldy);
2445 if (!IS_MOVING(oldx, oldy))
2449 if (element == EL_BLOCKED &&
2450 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2451 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2452 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2453 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2454 next_element = get_next_element(Feld[oldx][oldy]);
2456 RemoveField(oldx, oldy);
2457 RemoveField(newx, newy);
2459 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2461 if (next_element != EL_UNDEFINED)
2462 Feld[oldx][oldy] = next_element;
2464 DrawLevelField(oldx, oldy);
2465 DrawLevelField(newx, newy);
2468 void DrawDynamite(int x, int y)
2470 int sx = SCREENX(x), sy = SCREENY(y);
2471 int graphic = el2img(Feld[x][y]);
2474 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2477 if (IS_WALKABLE_INSIDE(Back[x][y]))
2481 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2482 else if (Store[x][y])
2483 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2485 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2488 if (Back[x][y] || Store[x][y])
2489 DrawGraphicThruMask(sx, sy, graphic, frame);
2491 DrawGraphic(sx, sy, graphic, frame);
2493 if (game.emulation == EMU_SUPAPLEX)
2494 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2495 else if (Store[x][y])
2496 DrawGraphicThruMask(sx, sy, graphic, frame);
2498 DrawGraphic(sx, sy, graphic, frame);
2502 void CheckDynamite(int x, int y)
2504 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2508 if (MovDelay[x][y] != 0)
2511 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2518 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2520 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2521 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2522 StopSound(SND_DYNAMITE_ACTIVE);
2524 StopSound(SND_DYNABOMB_ACTIVE);
2530 void RelocatePlayer(int x, int y, int element_raw)
2532 int element = (element_raw == EL_SP_MURPHY ? EL_PLAYER_1 : element_raw);
2533 struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
2534 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2535 boolean no_delay = (tape.index_search);
2536 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2537 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2539 if (player->GameOver) /* do not reanimate dead player */
2543 RemoveField(x, y); /* temporarily remove newly placed player */
2544 DrawLevelField(x, y);
2547 if (player->present)
2549 while (player->MovPos)
2551 ScrollPlayer(player, SCROLL_GO_ON);
2552 ScrollScreen(NULL, SCROLL_GO_ON);
2558 Delay(wait_delay_value);
2561 DrawPlayer(player); /* needed here only to cleanup last field */
2562 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2564 player->is_moving = FALSE;
2567 Feld[x][y] = element;
2568 InitPlayerField(x, y, element, TRUE);
2570 if (player == local_player)
2572 int scroll_xx = -999, scroll_yy = -999;
2574 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2577 int fx = FX, fy = FY;
2579 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2580 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2581 local_player->jx - MIDPOSX);
2583 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2584 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2585 local_player->jy - MIDPOSY);
2587 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2588 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2593 fx += dx * TILEX / 2;
2594 fy += dy * TILEY / 2;
2596 ScrollLevel(dx, dy);
2599 /* scroll in two steps of half tile size to make things smoother */
2600 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2602 Delay(wait_delay_value);
2604 /* scroll second step to align at full tile size */
2606 Delay(wait_delay_value);
2611 void Explode(int ex, int ey, int phase, int mode)
2618 /* !!! eliminate this variable !!! */
2619 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2624 int last_phase = num_phase * delay;
2625 int half_phase = (num_phase / 2) * delay;
2626 int first_phase_after_start = EX_PHASE_START + 1;
2630 if (game.explosions_delayed)
2632 ExplodeField[ex][ey] = mode;
2636 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2638 int center_element = Feld[ex][ey];
2641 /* --- This is only really needed (and now handled) in "Impact()". --- */
2642 /* do not explode moving elements that left the explode field in time */
2643 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2644 center_element == EL_EMPTY && (mode == EX_NORMAL || mode == EX_CENTER))
2648 if (mode == EX_NORMAL || mode == EX_CENTER)
2649 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2651 /* remove things displayed in background while burning dynamite */
2652 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2655 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2657 /* put moving element to center field (and let it explode there) */
2658 center_element = MovingOrBlocked2Element(ex, ey);
2659 RemoveMovingField(ex, ey);
2660 Feld[ex][ey] = center_element;
2664 last_phase = element_info[center_element].explosion_delay;
2667 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
2669 int xx = x - ex + 1;
2670 int yy = y - ey + 1;
2674 if (!IN_LEV_FIELD(x, y) || (mode != EX_NORMAL && (x != ex || y != ey)))
2677 if (!IN_LEV_FIELD(x, y) ||
2678 ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) &&
2679 (x != ex || y != ey)))
2683 element = Feld[x][y];
2685 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2687 element = MovingOrBlocked2Element(x, y);
2689 if (!IS_EXPLOSION_PROOF(element))
2690 RemoveMovingField(x, y);
2696 if (IS_EXPLOSION_PROOF(element))
2699 /* indestructible elements can only explode in center (but not flames) */
2700 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
2701 element == EL_FLAMES)
2706 if ((IS_INDESTRUCTIBLE(element) &&
2707 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
2708 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
2709 element == EL_FLAMES)
2713 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2715 if (IS_ACTIVE_BOMB(element))
2717 /* re-activate things under the bomb like gate or penguin */
2718 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
2725 /* save walkable background elements while explosion on same tile */
2727 if (IS_INDESTRUCTIBLE(element))
2728 Back[x][y] = element;
2730 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
2731 Back[x][y] = element;
2734 /* ignite explodable elements reached by other explosion */
2735 if (element == EL_EXPLOSION)
2736 element = Store2[x][y];
2739 if (AmoebaNr[x][y] &&
2740 (element == EL_AMOEBA_FULL ||
2741 element == EL_BD_AMOEBA ||
2742 element == EL_AMOEBA_GROWING))
2744 AmoebaCnt[AmoebaNr[x][y]]--;
2745 AmoebaCnt2[AmoebaNr[x][y]]--;
2751 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
2753 switch(StorePlayer[ex][ey])
2756 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
2759 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
2762 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
2766 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
2770 if (game.emulation == EMU_SUPAPLEX)
2771 Store[x][y] = EL_EMPTY;
2773 else if (center_element == EL_MOLE)
2774 Store[x][y] = EL_EMERALD_RED;
2775 else if (center_element == EL_PENGUIN)
2776 Store[x][y] = EL_EMERALD_PURPLE;
2777 else if (center_element == EL_BUG)
2778 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
2779 else if (center_element == EL_BD_BUTTERFLY)
2780 Store[x][y] = EL_BD_DIAMOND;
2781 else if (center_element == EL_SP_ELECTRON)
2782 Store[x][y] = EL_SP_INFOTRON;
2783 else if (center_element == EL_AMOEBA_TO_DIAMOND)
2784 Store[x][y] = level.amoeba_content;
2785 else if (center_element == EL_YAMYAM)
2786 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
2787 else if (IS_CUSTOM_ELEMENT(center_element) &&
2788 element_info[center_element].content[xx][yy] != EL_EMPTY)
2789 Store[x][y] = element_info[center_element].content[xx][yy];
2790 else if (element == EL_WALL_EMERALD)
2791 Store[x][y] = EL_EMERALD;
2792 else if (element == EL_WALL_DIAMOND)
2793 Store[x][y] = EL_DIAMOND;
2794 else if (element == EL_WALL_BD_DIAMOND)
2795 Store[x][y] = EL_BD_DIAMOND;
2796 else if (element == EL_WALL_EMERALD_YELLOW)
2797 Store[x][y] = EL_EMERALD_YELLOW;
2798 else if (element == EL_WALL_EMERALD_RED)
2799 Store[x][y] = EL_EMERALD_RED;
2800 else if (element == EL_WALL_EMERALD_PURPLE)
2801 Store[x][y] = EL_EMERALD_PURPLE;
2802 else if (element == EL_WALL_PEARL)
2803 Store[x][y] = EL_PEARL;
2804 else if (element == EL_WALL_CRYSTAL)
2805 Store[x][y] = EL_CRYSTAL;
2806 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
2807 Store[x][y] = element_info[element].content[1][1];
2809 Store[x][y] = EL_EMPTY;
2811 if (x != ex || y != ey ||
2812 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_BORDER)
2813 Store2[x][y] = element;
2816 if (AmoebaNr[x][y] &&
2817 (element == EL_AMOEBA_FULL ||
2818 element == EL_BD_AMOEBA ||
2819 element == EL_AMOEBA_GROWING))
2821 AmoebaCnt[AmoebaNr[x][y]]--;
2822 AmoebaCnt2[AmoebaNr[x][y]]--;
2828 MovDir[x][y] = MovPos[x][y] = 0;
2829 GfxDir[x][y] = MovDir[x][y];
2834 Feld[x][y] = EL_EXPLOSION;
2836 GfxElement[x][y] = center_element;
2838 GfxElement[x][y] = EL_UNDEFINED;
2841 ExplodePhase[x][y] = 1;
2843 ExplodeDelay[x][y] = last_phase;
2848 if (center_element == EL_YAMYAM)
2849 game.yamyam_content_nr =
2850 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
2862 last_phase = ExplodeDelay[x][y];
2865 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
2869 /* activate this even in non-DEBUG version until cause for crash in
2870 getGraphicAnimationFrame() (see below) is found and eliminated */
2874 if (GfxElement[x][y] == EL_UNDEFINED)
2877 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
2878 printf("Explode(): This should never happen!\n");
2881 GfxElement[x][y] = EL_EMPTY;
2887 border_element = Store2[x][y];
2888 if (IS_PLAYER(x, y))
2889 border_element = StorePlayer[x][y];
2891 if (phase == element_info[border_element].ignition_delay ||
2892 phase == last_phase)
2894 boolean border_explosion = FALSE;
2897 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
2899 if (IS_PLAYER(x, y))
2902 KillHeroUnlessExplosionProtected(x, y);
2903 border_explosion = TRUE;
2906 if (phase == last_phase)
2907 printf("::: IS_PLAYER\n");
2910 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
2912 Feld[x][y] = Store2[x][y];
2915 border_explosion = TRUE;
2918 if (phase == last_phase)
2919 printf("::: CAN_EXPLODE_BY_EXPLOSION\n");
2922 else if (border_element == EL_AMOEBA_TO_DIAMOND)
2924 AmoebeUmwandeln(x, y);
2926 border_explosion = TRUE;
2929 if (phase == last_phase)
2930 printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n",
2931 element_info[border_element].explosion_delay,
2932 element_info[border_element].ignition_delay,
2938 /* if an element just explodes due to another explosion (chain-reaction),
2939 do not immediately end the new explosion when it was the last frame of
2940 the explosion (as it would be done in the following "if"-statement!) */
2941 if (border_explosion && phase == last_phase)
2948 if (phase == first_phase_after_start)
2950 int element = Store2[x][y];
2952 if (element == EL_BLACK_ORB)
2954 Feld[x][y] = Store2[x][y];
2959 else if (phase == half_phase)
2961 int element = Store2[x][y];
2963 if (IS_PLAYER(x, y))
2964 KillHeroUnlessExplosionProtected(x, y);
2965 else if (CAN_EXPLODE_BY_EXPLOSION(element))
2967 Feld[x][y] = Store2[x][y];
2971 else if (element == EL_AMOEBA_TO_DIAMOND)
2972 AmoebeUmwandeln(x, y);
2976 if (phase == last_phase)
2980 element = Feld[x][y] = Store[x][y];
2981 Store[x][y] = Store2[x][y] = 0;
2982 GfxElement[x][y] = EL_UNDEFINED;
2984 /* player can escape from explosions and might therefore be still alive */
2985 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
2986 element <= EL_PLAYER_IS_EXPLODING_4)
2987 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
2989 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
2990 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
2991 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
2994 /* restore probably existing indestructible background element */
2995 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
2996 element = Feld[x][y] = Back[x][y];
2999 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3000 GfxDir[x][y] = MV_NO_MOVING;
3001 ChangeDelay[x][y] = 0;
3002 ChangePage[x][y] = -1;
3005 InitField_WithBug2(x, y, FALSE);
3007 InitField(x, y, FALSE);
3009 /* !!! not needed !!! */
3011 if (game.engine_version < VERSION_IDENT(3,0,9,0) &&
3012 CAN_MOVE(Feld[x][y]) && Feld[x][y] != EL_MOLE)
3015 if (CAN_MOVE(element))
3020 DrawLevelField(x, y);
3022 TestIfElementTouchesCustomElement(x, y);
3024 if (GFX_CRUMBLED(element))
3025 DrawLevelFieldCrumbledSandNeighbours(x, y);
3027 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3028 StorePlayer[x][y] = 0;
3030 if (ELEM_IS_PLAYER(element))
3031 RelocatePlayer(x, y, element);
3034 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3036 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3040 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3042 int stored = Store[x][y];
3043 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
3044 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
3047 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3050 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
3051 element_info[GfxElement[x][y]].token_name,
3056 DrawLevelFieldCrumbledSand(x, y);
3058 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3060 DrawLevelElement(x, y, Back[x][y]);
3061 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3063 else if (IS_WALKABLE_UNDER(Back[x][y]))
3065 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3066 DrawLevelElementThruMask(x, y, Back[x][y]);
3068 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3069 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3073 void DynaExplode(int ex, int ey)
3076 int dynabomb_element = Feld[ex][ey];
3077 int dynabomb_size = 1;
3078 boolean dynabomb_xl = FALSE;
3079 struct PlayerInfo *player;
3080 static int xy[4][2] =
3088 if (IS_ACTIVE_BOMB(dynabomb_element))
3090 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3091 dynabomb_size = player->dynabomb_size;
3092 dynabomb_xl = player->dynabomb_xl;
3093 player->dynabombs_left++;
3096 Explode(ex, ey, EX_PHASE_START, EX_CENTER);
3098 for (i = 0; i < NUM_DIRECTIONS; i++)
3100 for (j = 1; j <= dynabomb_size; j++)
3102 int x = ex + j * xy[i][0];
3103 int y = ey + j * xy[i][1];
3106 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3109 element = Feld[x][y];
3111 /* do not restart explosions of fields with active bombs */
3112 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3115 Explode(x, y, EX_PHASE_START, EX_BORDER);
3117 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
3118 if (element != EL_EMPTY &&
3119 element != EL_SAND &&
3120 element != EL_EXPLOSION &&
3127 void Bang(int x, int y)
3130 int element = MovingOrBlocked2Element(x, y);
3132 int element = Feld[x][y];
3136 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3138 if (IS_PLAYER(x, y))
3141 struct PlayerInfo *player = PLAYERINFO(x, y);
3143 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3144 player->element_nr);
3149 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
3151 if (game.emulation == EMU_SUPAPLEX)
3152 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
3154 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
3159 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
3167 case EL_BD_BUTTERFLY:
3170 case EL_DARK_YAMYAM:
3174 RaiseScoreElement(element);
3175 Explode(x, y, EX_PHASE_START, EX_NORMAL);
3177 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3178 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3179 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3180 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3181 case EL_DYNABOMB_INCREASE_NUMBER:
3182 case EL_DYNABOMB_INCREASE_SIZE:
3183 case EL_DYNABOMB_INCREASE_POWER:
3188 case EL_LAMP_ACTIVE:
3190 case EL_AMOEBA_TO_DIAMOND:
3192 if (IS_PLAYER(x, y))
3193 Explode(x, y, EX_PHASE_START, EX_NORMAL);
3195 Explode(x, y, EX_PHASE_START, EX_CENTER);
3198 if (CAN_EXPLODE_DYNA(element))
3200 else if (CAN_EXPLODE_1X1(element))
3201 Explode(x, y, EX_PHASE_START, EX_CENTER);
3203 Explode(x, y, EX_PHASE_START, EX_NORMAL);
3207 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
3210 void SplashAcid(int x, int y)
3213 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3214 (!IN_LEV_FIELD(x - 1, y - 2) ||
3215 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3216 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3218 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3219 (!IN_LEV_FIELD(x + 1, y - 2) ||
3220 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3221 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3223 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3225 /* input: position of element entering acid (obsolete) */
3227 int element = Feld[x][y];
3229 if (!IN_LEV_FIELD(x, y + 1) || Feld[x][y + 1] != EL_ACID)
3232 if (element != EL_ACID_SPLASH_LEFT &&
3233 element != EL_ACID_SPLASH_RIGHT)
3235 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3237 if (IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y) &&
3238 (!IN_LEV_FIELD(x - 1, y - 1) ||
3239 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 1))))
3240 Feld[x - 1][y] = EL_ACID_SPLASH_LEFT;
3242 if (IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y) &&
3243 (!IN_LEV_FIELD(x + 1, y - 1) ||
3244 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 1))))
3245 Feld[x + 1][y] = EL_ACID_SPLASH_RIGHT;
3250 static void InitBeltMovement()
3252 static int belt_base_element[4] =
3254 EL_CONVEYOR_BELT_1_LEFT,
3255 EL_CONVEYOR_BELT_2_LEFT,
3256 EL_CONVEYOR_BELT_3_LEFT,
3257 EL_CONVEYOR_BELT_4_LEFT
3259 static int belt_base_active_element[4] =
3261 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3262 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3263 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3264 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3269 /* set frame order for belt animation graphic according to belt direction */
3270 for (i = 0; i < NUM_BELTS; i++)
3274 for (j = 0; j < NUM_BELT_PARTS; j++)
3276 int element = belt_base_active_element[belt_nr] + j;
3277 int graphic = el2img(element);
3279 if (game.belt_dir[i] == MV_LEFT)
3280 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3282 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3286 for (y = 0; y < lev_fieldy; y++)
3288 for (x = 0; x < lev_fieldx; x++)
3290 int element = Feld[x][y];
3292 for (i = 0; i < NUM_BELTS; i++)
3294 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
3296 int e_belt_nr = getBeltNrFromBeltElement(element);
3299 if (e_belt_nr == belt_nr)
3301 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3303 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3311 static void ToggleBeltSwitch(int x, int y)
3313 static int belt_base_element[4] =
3315 EL_CONVEYOR_BELT_1_LEFT,
3316 EL_CONVEYOR_BELT_2_LEFT,
3317 EL_CONVEYOR_BELT_3_LEFT,
3318 EL_CONVEYOR_BELT_4_LEFT
3320 static int belt_base_active_element[4] =
3322 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3323 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3324 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3325 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3327 static int belt_base_switch_element[4] =
3329 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3330 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3331 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3332 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3334 static int belt_move_dir[4] =
3342 int element = Feld[x][y];
3343 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3344 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3345 int belt_dir = belt_move_dir[belt_dir_nr];
3348 if (!IS_BELT_SWITCH(element))
3351 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3352 game.belt_dir[belt_nr] = belt_dir;
3354 if (belt_dir_nr == 3)
3357 /* set frame order for belt animation graphic according to belt direction */
3358 for (i = 0; i < NUM_BELT_PARTS; i++)
3360 int element = belt_base_active_element[belt_nr] + i;
3361 int graphic = el2img(element);
3363 if (belt_dir == MV_LEFT)
3364 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3366 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3369 for (yy = 0; yy < lev_fieldy; yy++)
3371 for (xx = 0; xx < lev_fieldx; xx++)
3373 int element = Feld[xx][yy];
3375 if (IS_BELT_SWITCH(element))
3377 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3379 if (e_belt_nr == belt_nr)
3381 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3382 DrawLevelField(xx, yy);
3385 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
3387 int e_belt_nr = getBeltNrFromBeltElement(element);
3389 if (e_belt_nr == belt_nr)
3391 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3393 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3394 DrawLevelField(xx, yy);
3397 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
3399 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3401 if (e_belt_nr == belt_nr)
3403 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3405 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3406 DrawLevelField(xx, yy);
3413 static void ToggleSwitchgateSwitch(int x, int y)
3417 game.switchgate_pos = !game.switchgate_pos;
3419 for (yy = 0; yy < lev_fieldy; yy++)
3421 for (xx = 0; xx < lev_fieldx; xx++)
3423 int element = Feld[xx][yy];
3425 if (element == EL_SWITCHGATE_SWITCH_UP ||
3426 element == EL_SWITCHGATE_SWITCH_DOWN)
3428 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3429 DrawLevelField(xx, yy);
3431 else if (element == EL_SWITCHGATE_OPEN ||
3432 element == EL_SWITCHGATE_OPENING)
3434 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3436 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3438 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
3441 else if (element == EL_SWITCHGATE_CLOSED ||
3442 element == EL_SWITCHGATE_CLOSING)
3444 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3446 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3448 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
3455 static int getInvisibleActiveFromInvisibleElement(int element)
3457 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3458 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
3459 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
3463 static int getInvisibleFromInvisibleActiveElement(int element)
3465 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3466 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
3467 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
3471 static void RedrawAllLightSwitchesAndInvisibleElements()
3475 for (y = 0; y < lev_fieldy; y++)
3477 for (x = 0; x < lev_fieldx; x++)
3479 int element = Feld[x][y];
3481 if (element == EL_LIGHT_SWITCH &&
3482 game.light_time_left > 0)
3484 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3485 DrawLevelField(x, y);
3487 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3488 game.light_time_left == 0)
3490 Feld[x][y] = EL_LIGHT_SWITCH;
3491 DrawLevelField(x, y);
3493 else if (element == EL_INVISIBLE_STEELWALL ||
3494 element == EL_INVISIBLE_WALL ||
3495 element == EL_INVISIBLE_SAND)
3497 if (game.light_time_left > 0)
3498 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3500 DrawLevelField(x, y);
3502 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3503 element == EL_INVISIBLE_WALL_ACTIVE ||
3504 element == EL_INVISIBLE_SAND_ACTIVE)
3506 if (game.light_time_left == 0)
3507 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3509 DrawLevelField(x, y);
3515 static void ToggleLightSwitch(int x, int y)
3517 int element = Feld[x][y];
3519 game.light_time_left =
3520 (element == EL_LIGHT_SWITCH ?
3521 level.time_light * FRAMES_PER_SECOND : 0);
3523 RedrawAllLightSwitchesAndInvisibleElements();
3526 static void ActivateTimegateSwitch(int x, int y)
3530 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3532 for (yy = 0; yy < lev_fieldy; yy++)
3534 for (xx = 0; xx < lev_fieldx; xx++)
3536 int element = Feld[xx][yy];
3538 if (element == EL_TIMEGATE_CLOSED ||
3539 element == EL_TIMEGATE_CLOSING)
3541 Feld[xx][yy] = EL_TIMEGATE_OPENING;
3542 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3546 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3548 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3549 DrawLevelField(xx, yy);
3556 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3559 inline static int getElementMoveStepsize(int x, int y)
3561 int element = Feld[x][y];
3562 int direction = MovDir[x][y];
3563 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3564 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3565 int horiz_move = (dx != 0);
3566 int sign = (horiz_move ? dx : dy);
3567 int step = sign * element_info[element].move_stepsize;
3569 /* special values for move stepsize for spring and things on conveyor belt */
3573 if (element == EL_SPRING)
3574 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3575 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
3576 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3577 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3579 if (CAN_FALL(element) &&
3580 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3581 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3582 else if (element == EL_SPRING)
3583 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3590 void Impact(int x, int y)
3592 boolean lastline = (y == lev_fieldy-1);
3593 boolean object_hit = FALSE;
3594 boolean impact = (lastline || object_hit);
3595 int element = Feld[x][y];
3596 int smashed = EL_UNDEFINED;
3598 if (!lastline) /* check if element below was hit */
3600 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3603 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3604 MovDir[x][y + 1] != MV_DOWN ||
3605 MovPos[x][y + 1] <= TILEY / 2));
3608 object_hit = !IS_FREE(x, y + 1);
3611 /* do not smash moving elements that left the smashed field in time */
3612 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
3613 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3617 smashed = MovingOrBlocked2Element(x, y + 1);
3619 impact = (lastline || object_hit);
3622 if (!lastline && smashed == EL_ACID) /* element falls into acid */
3624 SplashAcid(x, y + 1);
3628 /* only reset graphic animation if graphic really changes after impact */
3630 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3632 ResetGfxAnimation(x, y);
3633 DrawLevelField(x, y);
3636 if (impact && CAN_EXPLODE_IMPACT(element))
3641 else if (impact && element == EL_PEARL)
3643 Feld[x][y] = EL_PEARL_BREAKING;
3644 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3647 else if (impact && CheckElementChange(x, y, element, CE_IMPACT))
3649 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3654 if (impact && element == EL_AMOEBA_DROP)
3656 if (object_hit && IS_PLAYER(x, y + 1))
3657 KillHeroUnlessEnemyProtected(x, y + 1);
3658 else if (object_hit && smashed == EL_PENGUIN)
3662 Feld[x][y] = EL_AMOEBA_GROWING;
3663 Store[x][y] = EL_AMOEBA_WET;
3665 ResetRandomAnimationValue(x, y);
3670 if (object_hit) /* check which object was hit */
3672 if (CAN_PASS_MAGIC_WALL(element) &&
3673 (smashed == EL_MAGIC_WALL ||
3674 smashed == EL_BD_MAGIC_WALL))
3677 int activated_magic_wall =
3678 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3679 EL_BD_MAGIC_WALL_ACTIVE);
3681 /* activate magic wall / mill */
3682 for (yy = 0; yy < lev_fieldy; yy++)
3683 for (xx = 0; xx < lev_fieldx; xx++)
3684 if (Feld[xx][yy] == smashed)
3685 Feld[xx][yy] = activated_magic_wall;
3687 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3688 game.magic_wall_active = TRUE;
3690 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
3691 SND_MAGIC_WALL_ACTIVATING :
3692 SND_BD_MAGIC_WALL_ACTIVATING));
3695 if (IS_PLAYER(x, y + 1))
3697 if (CAN_SMASH_PLAYER(element))
3699 KillHeroUnlessEnemyProtected(x, y + 1);
3703 else if (smashed == EL_PENGUIN)
3705 if (CAN_SMASH_PLAYER(element))
3711 else if (element == EL_BD_DIAMOND)
3713 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3719 else if (((element == EL_SP_INFOTRON ||
3720 element == EL_SP_ZONK) &&
3721 (smashed == EL_SP_SNIKSNAK ||
3722 smashed == EL_SP_ELECTRON ||
3723 smashed == EL_SP_DISK_ORANGE)) ||
3724 (element == EL_SP_INFOTRON &&
3725 smashed == EL_SP_DISK_YELLOW))
3731 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
3737 else if (CAN_SMASH_EVERYTHING(element))
3739 if (IS_CLASSIC_ENEMY(smashed) ||
3740 CAN_EXPLODE_SMASHED(smashed))
3745 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3747 if (smashed == EL_LAMP ||
3748 smashed == EL_LAMP_ACTIVE)
3753 else if (smashed == EL_NUT)
3755 Feld[x][y + 1] = EL_NUT_BREAKING;
3756 PlayLevelSound(x, y, SND_NUT_BREAKING);
3757 RaiseScoreElement(EL_NUT);
3760 else if (smashed == EL_PEARL)
3762 Feld[x][y + 1] = EL_PEARL_BREAKING;
3763 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3766 else if (smashed == EL_DIAMOND)
3768 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3769 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
3772 else if (IS_BELT_SWITCH(smashed))
3774 ToggleBeltSwitch(x, y + 1);
3776 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3777 smashed == EL_SWITCHGATE_SWITCH_DOWN)
3779 ToggleSwitchgateSwitch(x, y + 1);
3781 else if (smashed == EL_LIGHT_SWITCH ||
3782 smashed == EL_LIGHT_SWITCH_ACTIVE)
3784 ToggleLightSwitch(x, y + 1);
3788 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3790 CheckTriggeredElementChangeSide(x, y + 1, smashed,
3791 CE_OTHER_IS_SWITCHING, CH_SIDE_TOP);
3792 CheckElementChangeSide(x, y + 1, smashed, CE_SWITCHED, CH_SIDE_TOP);
3797 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3802 /* play sound of magic wall / mill */
3804 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3805 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3807 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3808 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
3809 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3810 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
3815 /* play sound of object that hits the ground */
3816 if (lastline || object_hit)
3817 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3820 inline static void TurnRoundExt(int x, int y)
3832 { 0, 0 }, { 0, 0 }, { 0, 0 },
3837 int left, right, back;
3841 { MV_DOWN, MV_UP, MV_RIGHT },
3842 { MV_UP, MV_DOWN, MV_LEFT },
3844 { MV_LEFT, MV_RIGHT, MV_DOWN },
3848 { MV_RIGHT, MV_LEFT, MV_UP }
3851 int element = Feld[x][y];
3852 int move_pattern = element_info[element].move_pattern;
3854 int old_move_dir = MovDir[x][y];
3855 int left_dir = turn[old_move_dir].left;
3856 int right_dir = turn[old_move_dir].right;
3857 int back_dir = turn[old_move_dir].back;
3859 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
3860 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
3861 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
3862 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
3864 int left_x = x + left_dx, left_y = y + left_dy;
3865 int right_x = x + right_dx, right_y = y + right_dy;
3866 int move_x = x + move_dx, move_y = y + move_dy;
3870 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3872 TestIfBadThingTouchesOtherBadThing(x, y);
3874 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
3875 MovDir[x][y] = right_dir;
3876 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
3877 MovDir[x][y] = left_dir;
3879 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
3881 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
3884 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3885 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3887 TestIfBadThingTouchesOtherBadThing(x, y);
3889 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
3890 MovDir[x][y] = left_dir;
3891 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
3892 MovDir[x][y] = right_dir;
3894 if ((element == EL_SPACESHIP ||
3895 element == EL_SP_SNIKSNAK ||
3896 element == EL_SP_ELECTRON)
3897 && MovDir[x][y] != old_move_dir)
3899 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
3902 else if (element == EL_YAMYAM)
3904 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3905 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3907 if (can_turn_left && can_turn_right)
3908 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3909 else if (can_turn_left)
3910 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3911 else if (can_turn_right)
3912 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3914 MovDir[x][y] = back_dir;
3916 MovDelay[x][y] = 16 + 16 * RND(3);
3918 else if (element == EL_DARK_YAMYAM)
3920 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3921 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3923 if (can_turn_left && can_turn_right)
3924 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3925 else if (can_turn_left)
3926 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3927 else if (can_turn_right)
3928 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3930 MovDir[x][y] = back_dir;
3932 MovDelay[x][y] = 16 + 16 * RND(3);
3934 else if (element == EL_PACMAN)
3936 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(left_x, left_y);
3937 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(right_x, right_y);
3939 if (can_turn_left && can_turn_right)
3940 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3941 else if (can_turn_left)
3942 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3943 else if (can_turn_right)
3944 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3946 MovDir[x][y] = back_dir;
3948 MovDelay[x][y] = 6 + RND(40);
3950 else if (element == EL_PIG)
3952 boolean can_turn_left = PIG_CAN_ENTER_FIELD(left_x, left_y);
3953 boolean can_turn_right = PIG_CAN_ENTER_FIELD(right_x, right_y);
3954 boolean can_move_on = PIG_CAN_ENTER_FIELD(move_x, move_y);
3955 boolean should_turn_left, should_turn_right, should_move_on;
3957 int rnd = RND(rnd_value);
3959 should_turn_left = (can_turn_left &&
3961 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
3962 y + back_dy + left_dy)));
3963 should_turn_right = (can_turn_right &&
3965 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
3966 y + back_dy + right_dy)));
3967 should_move_on = (can_move_on &&
3970 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
3971 y + move_dy + left_dy) ||
3972 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
3973 y + move_dy + right_dy)));
3975 if (should_turn_left || should_turn_right || should_move_on)
3977 if (should_turn_left && should_turn_right && should_move_on)
3978 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
3979 rnd < 2 * rnd_value / 3 ? right_dir :
3981 else if (should_turn_left && should_turn_right)
3982 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3983 else if (should_turn_left && should_move_on)
3984 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
3985 else if (should_turn_right && should_move_on)
3986 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
3987 else if (should_turn_left)
3988 MovDir[x][y] = left_dir;
3989 else if (should_turn_right)
3990 MovDir[x][y] = right_dir;
3991 else if (should_move_on)
3992 MovDir[x][y] = old_move_dir;
3994 else if (can_move_on && rnd > rnd_value / 8)
3995 MovDir[x][y] = old_move_dir;
3996 else if (can_turn_left && can_turn_right)
3997 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3998 else if (can_turn_left && rnd > rnd_value / 8)
3999 MovDir[x][y] = left_dir;
4000 else if (can_turn_right && rnd > rnd_value/8)
4001 MovDir[x][y] = right_dir;
4003 MovDir[x][y] = back_dir;
4005 xx = x + move_xy[MovDir[x][y]].x;
4006 yy = y + move_xy[MovDir[x][y]].y;
4008 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
4009 MovDir[x][y] = old_move_dir;
4013 else if (element == EL_DRAGON)
4015 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(left_x, left_y);
4016 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(right_x, right_y);
4017 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(move_x, move_y);
4019 int rnd = RND(rnd_value);
4022 if (FrameCounter < 1 && x == 0 && y == 29)
4023 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4026 if (can_move_on && rnd > rnd_value / 8)
4027 MovDir[x][y] = old_move_dir;
4028 else if (can_turn_left && can_turn_right)
4029 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4030 else if (can_turn_left && rnd > rnd_value / 8)
4031 MovDir[x][y] = left_dir;
4032 else if (can_turn_right && rnd > rnd_value / 8)
4033 MovDir[x][y] = right_dir;
4035 MovDir[x][y] = back_dir;
4037 xx = x + move_xy[MovDir[x][y]].x;
4038 yy = y + move_xy[MovDir[x][y]].y;
4041 if (FrameCounter < 1 && x == 0 && y == 29)
4042 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
4043 xx, yy, Feld[xx][yy],
4048 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4049 MovDir[x][y] = old_move_dir;
4051 if (!IS_FREE(xx, yy))
4052 MovDir[x][y] = old_move_dir;
4056 if (FrameCounter < 1 && x == 0 && y == 29)
4057 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4062 else if (element == EL_MOLE)
4064 boolean can_move_on =
4065 (MOLE_CAN_ENTER_FIELD(move_x, move_y,
4066 IS_AMOEBOID(Feld[move_x][move_y]) ||
4067 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4070 boolean can_turn_left =
4071 (MOLE_CAN_ENTER_FIELD(left_x, left_y,
4072 IS_AMOEBOID(Feld[left_x][left_y])));
4074 boolean can_turn_right =
4075 (MOLE_CAN_ENTER_FIELD(right_x, right_y,
4076 IS_AMOEBOID(Feld[right_x][right_y])));
4078 if (can_turn_left && can_turn_right)
4079 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4080 else if (can_turn_left)
4081 MovDir[x][y] = left_dir;
4083 MovDir[x][y] = right_dir;
4086 if (MovDir[x][y] != old_move_dir)
4089 else if (element == EL_BALLOON)
4091 MovDir[x][y] = game.balloon_dir;
4094 else if (element == EL_SPRING)
4097 if (MovDir[x][y] & MV_HORIZONTAL &&
4098 !SPRING_CAN_ENTER_FIELD(move_x, move_y))
4099 MovDir[x][y] = MV_NO_MOVING;
4101 if (MovDir[x][y] & MV_HORIZONTAL &&
4102 (!SPRING_CAN_ENTER_FIELD(move_x, move_y) ||
4103 SPRING_CAN_ENTER_FIELD(x, y + 1)))
4104 MovDir[x][y] = MV_NO_MOVING;
4109 else if (element == EL_ROBOT ||
4110 element == EL_SATELLITE ||
4111 element == EL_PENGUIN)
4113 int attr_x = -1, attr_y = -1;
4124 for (i = 0; i < MAX_PLAYERS; i++)
4126 struct PlayerInfo *player = &stored_player[i];
4127 int jx = player->jx, jy = player->jy;
4129 if (!player->active)
4133 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4141 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
4147 if (element == EL_PENGUIN)
4150 static int xy[4][2] =
4158 for (i = 0; i < NUM_DIRECTIONS; i++)
4160 int ex = x + xy[i][0];
4161 int ey = y + xy[i][1];
4163 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4172 MovDir[x][y] = MV_NO_MOVING;
4174 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4175 else if (attr_x > x)
4176 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4178 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4179 else if (attr_y > y)
4180 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4182 if (element == EL_ROBOT)
4186 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4187 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4188 Moving2Blocked(x, y, &newx, &newy);
4190 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4191 MovDelay[x][y] = 8 + 8 * !RND(3);
4193 MovDelay[x][y] = 16;
4195 else if (element == EL_PENGUIN)
4201 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4203 boolean first_horiz = RND(2);
4204 int new_move_dir = MovDir[x][y];
4207 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4208 Moving2Blocked(x, y, &newx, &newy);
4210 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
4214 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4215 Moving2Blocked(x, y, &newx, &newy);
4217 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
4220 MovDir[x][y] = old_move_dir;
4224 else /* (element == EL_SATELLITE) */
4230 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4232 boolean first_horiz = RND(2);
4233 int new_move_dir = MovDir[x][y];
4236 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4237 Moving2Blocked(x, y, &newx, &newy);
4239 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
4243 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4244 Moving2Blocked(x, y, &newx, &newy);
4246 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
4249 MovDir[x][y] = old_move_dir;
4254 else if (move_pattern == MV_TURNING_LEFT ||
4255 move_pattern == MV_TURNING_RIGHT ||
4256 move_pattern == MV_TURNING_LEFT_RIGHT ||
4257 move_pattern == MV_TURNING_RIGHT_LEFT ||
4258 move_pattern == MV_TURNING_RANDOM ||
4259 move_pattern == MV_ALL_DIRECTIONS)
4261 boolean can_turn_left =
4262 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4263 boolean can_turn_right =
4264 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4266 if (move_pattern == MV_TURNING_LEFT)
4267 MovDir[x][y] = left_dir;
4268 else if (move_pattern == MV_TURNING_RIGHT)
4269 MovDir[x][y] = right_dir;
4270 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4271 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4272 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4273 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4274 else if (move_pattern == MV_TURNING_RANDOM)
4275 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4276 can_turn_right && !can_turn_left ? right_dir :
4277 RND(2) ? left_dir : right_dir);
4278 else if (can_turn_left && can_turn_right)
4279 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4280 else if (can_turn_left)
4281 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4282 else if (can_turn_right)
4283 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4285 MovDir[x][y] = back_dir;
4287 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4289 else if (move_pattern == MV_HORIZONTAL ||
4290 move_pattern == MV_VERTICAL)
4292 if (move_pattern & old_move_dir)
4293 MovDir[x][y] = back_dir;
4294 else if (move_pattern == MV_HORIZONTAL)
4295 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4296 else if (move_pattern == MV_VERTICAL)
4297 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4299 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4301 else if (move_pattern & MV_ANY_DIRECTION)
4303 MovDir[x][y] = move_pattern;
4304 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4306 else if (move_pattern == MV_ALONG_LEFT_SIDE)
4308 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4309 MovDir[x][y] = left_dir;
4310 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4311 MovDir[x][y] = right_dir;
4313 if (MovDir[x][y] != old_move_dir)
4314 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4316 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4318 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4319 MovDir[x][y] = right_dir;
4320 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4321 MovDir[x][y] = left_dir;
4323 if (MovDir[x][y] != old_move_dir)
4324 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4326 else if (move_pattern == MV_TOWARDS_PLAYER ||
4327 move_pattern == MV_AWAY_FROM_PLAYER)
4329 int attr_x = -1, attr_y = -1;
4331 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4342 for (i = 0; i < MAX_PLAYERS; i++)
4344 struct PlayerInfo *player = &stored_player[i];
4345 int jx = player->jx, jy = player->jy;
4347 if (!player->active)
4351 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4359 MovDir[x][y] = MV_NO_MOVING;
4361 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4362 else if (attr_x > x)
4363 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4365 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4366 else if (attr_y > y)
4367 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4369 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4371 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4373 boolean first_horiz = RND(2);
4374 int new_move_dir = MovDir[x][y];
4377 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4378 Moving2Blocked(x, y, &newx, &newy);
4380 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4384 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4385 Moving2Blocked(x, y, &newx, &newy);
4387 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4390 MovDir[x][y] = old_move_dir;
4393 else if (move_pattern == MV_WHEN_PUSHED ||
4394 move_pattern == MV_WHEN_DROPPED)
4396 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4397 MovDir[x][y] = MV_NO_MOVING;
4401 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4403 static int test_xy[7][2] =
4413 static int test_dir[7] =
4423 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4424 int move_preference = -1000000; /* start with very low preference */
4425 int new_move_dir = MV_NO_MOVING;
4426 int start_test = RND(4);
4429 for (i = 0; i < NUM_DIRECTIONS; i++)
4431 int move_dir = test_dir[start_test + i];
4432 int move_dir_preference;
4434 xx = x + test_xy[start_test + i][0];
4435 yy = y + test_xy[start_test + i][1];
4437 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4438 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4440 new_move_dir = move_dir;
4445 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4448 move_dir_preference = -1 * RunnerVisit[xx][yy];
4449 if (hunter_mode && PlayerVisit[xx][yy] > 0)
4450 move_dir_preference = PlayerVisit[xx][yy];
4452 if (move_dir_preference > move_preference)
4454 /* prefer field that has not been visited for the longest time */
4455 move_preference = move_dir_preference;
4456 new_move_dir = move_dir;
4458 else if (move_dir_preference == move_preference &&
4459 move_dir == old_move_dir)
4461 /* prefer last direction when all directions are preferred equally */
4462 move_preference = move_dir_preference;
4463 new_move_dir = move_dir;
4467 MovDir[x][y] = new_move_dir;
4468 if (old_move_dir != new_move_dir)
4473 static void TurnRound(int x, int y)
4475 int direction = MovDir[x][y];
4478 GfxDir[x][y] = MovDir[x][y];
4484 GfxDir[x][y] = MovDir[x][y];
4487 if (direction != MovDir[x][y])
4492 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4495 GfxAction[x][y] = ACTION_WAITING;
4499 static boolean JustBeingPushed(int x, int y)
4503 for (i = 0; i < MAX_PLAYERS; i++)
4505 struct PlayerInfo *player = &stored_player[i];
4507 if (player->active && player->is_pushing && player->MovPos)
4509 int next_jx = player->jx + (player->jx - player->last_jx);
4510 int next_jy = player->jy + (player->jy - player->last_jy);
4512 if (x == next_jx && y == next_jy)
4520 void StartMoving(int x, int y)
4523 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
4525 boolean started_moving = FALSE; /* some elements can fall _and_ move */
4526 int element = Feld[x][y];
4532 if (MovDelay[x][y] == 0)
4533 GfxAction[x][y] = ACTION_DEFAULT;
4535 /* !!! this should be handled more generic (not only for mole) !!! */
4536 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
4537 GfxAction[x][y] = ACTION_DEFAULT;
4540 if (CAN_FALL(element) && y < lev_fieldy - 1)
4542 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
4543 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
4544 if (JustBeingPushed(x, y))
4547 if (element == EL_QUICKSAND_FULL)
4549 if (IS_FREE(x, y + 1))
4551 InitMovingField(x, y, MV_DOWN);
4552 started_moving = TRUE;
4554 Feld[x][y] = EL_QUICKSAND_EMPTYING;
4555 Store[x][y] = EL_ROCK;
4557 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4559 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
4562 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4564 if (!MovDelay[x][y])
4565 MovDelay[x][y] = TILEY + 1;
4574 Feld[x][y] = EL_QUICKSAND_EMPTY;
4575 Feld[x][y + 1] = EL_QUICKSAND_FULL;
4576 Store[x][y + 1] = Store[x][y];
4579 PlayLevelSoundAction(x, y, ACTION_FILLING);
4581 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4585 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4586 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4588 InitMovingField(x, y, MV_DOWN);
4589 started_moving = TRUE;
4591 Feld[x][y] = EL_QUICKSAND_FILLING;
4592 Store[x][y] = element;
4594 PlayLevelSoundAction(x, y, ACTION_FILLING);
4596 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4599 else if (element == EL_MAGIC_WALL_FULL)
4601 if (IS_FREE(x, y + 1))
4603 InitMovingField(x, y, MV_DOWN);
4604 started_moving = TRUE;
4606 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4607 Store[x][y] = EL_CHANGED(Store[x][y]);
4609 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4611 if (!MovDelay[x][y])
4612 MovDelay[x][y] = TILEY/4 + 1;
4621 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4622 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4623 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4627 else if (element == EL_BD_MAGIC_WALL_FULL)
4629 if (IS_FREE(x, y + 1))
4631 InitMovingField(x, y, MV_DOWN);
4632 started_moving = TRUE;
4634 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4635 Store[x][y] = EL_CHANGED2(Store[x][y]);
4637 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4639 if (!MovDelay[x][y])
4640 MovDelay[x][y] = TILEY/4 + 1;
4649 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
4650 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
4651 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
4655 else if (CAN_PASS_MAGIC_WALL(element) &&
4656 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4657 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4659 InitMovingField(x, y, MV_DOWN);
4660 started_moving = TRUE;
4663 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
4664 EL_BD_MAGIC_WALL_FILLING);
4665 Store[x][y] = element;
4668 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
4670 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
4673 SplashAcid(x, y + 1);
4675 InitMovingField(x, y, MV_DOWN);
4676 started_moving = TRUE;
4678 Store[x][y] = EL_ACID;
4680 /* !!! TEST !!! better use "_FALLING" etc. !!! */
4681 GfxAction[x][y + 1] = ACTION_ACTIVE;
4685 else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
4686 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
4687 (Feld[x][y + 1] == EL_BLOCKED)) ||
4688 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
4689 CAN_SMASH(element) && WasJustFalling[x][y] &&
4690 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
4694 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
4695 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4696 WasJustMoving[x][y] && !Pushed[x][y + 1])
4698 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4699 WasJustMoving[x][y])
4704 /* this is needed for a special case not covered by calling "Impact()"
4705 from "ContinueMoving()": if an element moves to a tile directly below
4706 another element which was just falling on that tile (which was empty
4707 in the previous frame), the falling element above would just stop
4708 instead of smashing the element below (in previous version, the above
4709 element was just checked for "moving" instead of "falling", resulting
4710 in incorrect smashes caused by horizontal movement of the above
4711 element; also, the case of the player being the element to smash was
4712 simply not covered here... :-/ ) */
4715 WasJustMoving[x][y] = 0;
4716 WasJustFalling[x][y] = 0;
4721 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
4723 if (MovDir[x][y] == MV_NO_MOVING)
4725 InitMovingField(x, y, MV_DOWN);
4726 started_moving = TRUE;
4729 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
4731 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
4732 MovDir[x][y] = MV_DOWN;
4734 InitMovingField(x, y, MV_DOWN);
4735 started_moving = TRUE;
4737 else if (element == EL_AMOEBA_DROP)
4739 Feld[x][y] = EL_AMOEBA_GROWING;
4740 Store[x][y] = EL_AMOEBA_WET;
4742 /* Store[x][y + 1] must be zero, because:
4743 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
4746 #if OLD_GAME_BEHAVIOUR
4747 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
4749 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
4750 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4751 element != EL_DX_SUPABOMB)
4754 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4755 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4756 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4757 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4760 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
4761 (IS_FREE(x - 1, y + 1) ||
4762 Feld[x - 1][y + 1] == EL_ACID));
4763 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4764 (IS_FREE(x + 1, y + 1) ||
4765 Feld[x + 1][y + 1] == EL_ACID));
4766 boolean can_fall_any = (can_fall_left || can_fall_right);
4767 boolean can_fall_both = (can_fall_left && can_fall_right);
4769 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4771 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4773 if (slippery_type == SLIPPERY_ONLY_LEFT)
4774 can_fall_right = FALSE;
4775 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4776 can_fall_left = FALSE;
4777 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4778 can_fall_right = FALSE;
4779 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4780 can_fall_left = FALSE;
4782 can_fall_any = (can_fall_left || can_fall_right);
4783 can_fall_both = (can_fall_left && can_fall_right);
4788 if (can_fall_both &&
4789 (game.emulation != EMU_BOULDERDASH &&
4790 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
4791 can_fall_left = !(can_fall_right = RND(2));
4793 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4794 started_moving = TRUE;
4798 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
4800 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4803 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
4804 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4805 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4806 int belt_dir = game.belt_dir[belt_nr];
4808 if ((belt_dir == MV_LEFT && left_is_free) ||
4809 (belt_dir == MV_RIGHT && right_is_free))
4812 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
4815 InitMovingField(x, y, belt_dir);
4816 started_moving = TRUE;
4819 Pushed[x][y] = TRUE;
4820 Pushed[nextx][y] = TRUE;
4823 GfxAction[x][y] = ACTION_DEFAULT;
4827 MovDir[x][y] = 0; /* if element was moving, stop it */
4832 /* not "else if" because of elements that can fall and move (EL_SPRING) */
4833 if (CAN_MOVE(element) && !started_moving)
4835 int move_pattern = element_info[element].move_pattern;
4838 Moving2Blocked(x, y, &newx, &newy);
4841 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4844 if ((element == EL_SATELLITE ||
4845 element == EL_BALLOON ||
4846 element == EL_SPRING)
4847 && JustBeingPushed(x, y))
4852 if (game.engine_version >= VERSION_IDENT(3,0,9,0) &&
4853 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
4854 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
4857 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
4858 element, element_info[element].token_name,
4859 WasJustMoving[x][y],
4860 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
4861 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
4862 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
4863 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
4867 WasJustMoving[x][y] = 0;
4870 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
4873 if (Feld[x][y] != element) /* element has changed */
4875 element = Feld[x][y];
4876 move_pattern = element_info[element].move_pattern;
4878 if (!CAN_MOVE(element))
4882 if (Feld[x][y] != element) /* element has changed */
4890 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
4891 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
4893 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
4895 Moving2Blocked(x, y, &newx, &newy);
4896 if (Feld[newx][newy] == EL_BLOCKED)
4897 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
4903 if (FrameCounter < 1 && x == 0 && y == 29)
4904 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4907 if (!MovDelay[x][y]) /* start new movement phase */
4909 /* all objects that can change their move direction after each step
4910 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4912 if (element != EL_YAMYAM &&
4913 element != EL_DARK_YAMYAM &&
4914 element != EL_PACMAN &&
4915 !(move_pattern & MV_ANY_DIRECTION) &&
4916 move_pattern != MV_TURNING_LEFT &&
4917 move_pattern != MV_TURNING_RIGHT &&
4918 move_pattern != MV_TURNING_LEFT_RIGHT &&
4919 move_pattern != MV_TURNING_RIGHT_LEFT &&
4920 move_pattern != MV_TURNING_RANDOM)
4925 if (FrameCounter < 1 && x == 0 && y == 29)
4926 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
4929 if (MovDelay[x][y] && (element == EL_BUG ||
4930 element == EL_SPACESHIP ||
4931 element == EL_SP_SNIKSNAK ||
4932 element == EL_SP_ELECTRON ||
4933 element == EL_MOLE))
4934 DrawLevelField(x, y);
4938 if (MovDelay[x][y]) /* wait some time before next movement */
4943 if (element == EL_YAMYAM)
4946 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
4947 DrawLevelElementAnimation(x, y, element);
4951 if (MovDelay[x][y]) /* element still has to wait some time */
4954 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
4955 ResetGfxAnimation(x, y);
4959 if (GfxAction[x][y] != ACTION_WAITING)
4960 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
4962 GfxAction[x][y] = ACTION_WAITING;
4966 if (element == EL_ROBOT ||
4968 element == EL_PACMAN ||
4970 element == EL_YAMYAM ||
4971 element == EL_DARK_YAMYAM)
4974 DrawLevelElementAnimation(x, y, element);
4976 DrawLevelElementAnimationIfNeeded(x, y, element);
4978 PlayLevelSoundAction(x, y, ACTION_WAITING);
4980 else if (element == EL_SP_ELECTRON)
4981 DrawLevelElementAnimationIfNeeded(x, y, element);
4982 else if (element == EL_DRAGON)
4985 int dir = MovDir[x][y];
4986 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
4987 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
4988 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
4989 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
4990 dir == MV_UP ? IMG_FLAMES_1_UP :
4991 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
4992 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4995 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
4998 GfxAction[x][y] = ACTION_ATTACKING;
5000 if (IS_PLAYER(x, y))
5001 DrawPlayerField(x, y);
5003 DrawLevelField(x, y);
5005 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5007 for (i = 1; i <= 3; i++)
5009 int xx = x + i * dx;
5010 int yy = y + i * dy;
5011 int sx = SCREENX(xx);
5012 int sy = SCREENY(yy);
5013 int flame_graphic = graphic + (i - 1);
5015 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5020 int flamed = MovingOrBlocked2Element(xx, yy);
5022 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5025 RemoveMovingField(xx, yy);
5027 Feld[xx][yy] = EL_FLAMES;
5028 if (IN_SCR_FIELD(sx, sy))
5030 DrawLevelFieldCrumbledSand(xx, yy);
5031 DrawGraphic(sx, sy, flame_graphic, frame);
5036 if (Feld[xx][yy] == EL_FLAMES)
5037 Feld[xx][yy] = EL_EMPTY;
5038 DrawLevelField(xx, yy);
5043 if (MovDelay[x][y]) /* element still has to wait some time */
5045 PlayLevelSoundAction(x, y, ACTION_WAITING);
5051 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
5052 for all other elements GfxAction will be set by InitMovingField() */
5053 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
5054 GfxAction[x][y] = ACTION_MOVING;
5058 /* now make next step */
5060 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5062 if (DONT_COLLIDE_WITH(element) &&
5063 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5064 !PLAYER_ENEMY_PROTECTED(newx, newy))
5067 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
5071 /* player killed by element which is deadly when colliding with */
5073 KillHero(PLAYERINFO(newx, newy));
5080 else if (CAN_MOVE_INTO_ACID(element) &&
5081 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5082 (MovDir[x][y] == MV_DOWN ||
5083 game.engine_version > VERSION_IDENT(3,0,8,0)))
5085 else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
5086 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
5090 else if ((element == EL_PENGUIN ||
5091 element == EL_ROBOT ||
5092 element == EL_SATELLITE ||
5093 element == EL_BALLOON ||
5094 IS_CUSTOM_ELEMENT(element)) &&
5095 IN_LEV_FIELD(newx, newy) &&
5096 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
5099 SplashAcid(newx, newy);
5100 Store[x][y] = EL_ACID;
5102 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5104 if (Feld[newx][newy] == EL_EXIT_OPEN)
5108 DrawLevelField(x, y);
5110 Feld[x][y] = EL_EMPTY;
5111 DrawLevelField(x, y);
5114 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5115 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5116 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5118 local_player->friends_still_needed--;
5119 if (!local_player->friends_still_needed &&
5120 !local_player->GameOver && AllPlayersGone)
5121 local_player->LevelSolved = local_player->GameOver = TRUE;
5125 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5127 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
5128 DrawLevelField(newx, newy);
5130 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5132 else if (!IS_FREE(newx, newy))
5134 GfxAction[x][y] = ACTION_WAITING;
5136 if (IS_PLAYER(x, y))
5137 DrawPlayerField(x, y);
5139 DrawLevelField(x, y);
5144 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5146 if (IS_FOOD_PIG(Feld[newx][newy]))
5148 if (IS_MOVING(newx, newy))
5149 RemoveMovingField(newx, newy);
5152 Feld[newx][newy] = EL_EMPTY;
5153 DrawLevelField(newx, newy);
5156 PlayLevelSound(x, y, SND_PIG_DIGGING);
5158 else if (!IS_FREE(newx, newy))
5160 if (IS_PLAYER(x, y))
5161 DrawPlayerField(x, y);
5163 DrawLevelField(x, y);
5172 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
5175 else if (IS_CUSTOM_ELEMENT(element) &&
5176 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
5180 !IS_FREE(newx, newy)
5185 int new_element = Feld[newx][newy];
5188 printf("::: '%s' digs '%s' [%d]\n",
5189 element_info[element].token_name,
5190 element_info[Feld[newx][newy]].token_name,
5191 StorePlayer[newx][newy]);
5194 if (!IS_FREE(newx, newy))
5196 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5197 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5200 /* no element can dig solid indestructible elements */
5201 if (IS_INDESTRUCTIBLE(new_element) &&
5202 !IS_DIGGABLE(new_element) &&
5203 !IS_COLLECTIBLE(new_element))
5206 if (AmoebaNr[newx][newy] &&
5207 (new_element == EL_AMOEBA_FULL ||
5208 new_element == EL_BD_AMOEBA ||
5209 new_element == EL_AMOEBA_GROWING))
5211 AmoebaCnt[AmoebaNr[newx][newy]]--;
5212 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5215 if (IS_MOVING(newx, newy))
5216 RemoveMovingField(newx, newy);
5219 RemoveField(newx, newy);
5220 DrawLevelField(newx, newy);
5223 PlayLevelSoundAction(x, y, action);
5226 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5227 element_info[element].can_leave_element = TRUE;
5229 if (move_pattern & MV_MAZE_RUNNER_STYLE)
5231 RunnerVisit[x][y] = FrameCounter;
5232 PlayerVisit[x][y] /= 8; /* expire player visit path */
5238 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5240 if (!IS_FREE(newx, newy))
5242 if (IS_PLAYER(x, y))
5243 DrawPlayerField(x, y);
5245 DrawLevelField(x, y);
5251 boolean wanna_flame = !RND(10);
5252 int dx = newx - x, dy = newy - y;
5253 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5254 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5255 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5256 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5257 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5258 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5261 IS_CLASSIC_ENEMY(element1) ||
5262 IS_CLASSIC_ENEMY(element2)) &&
5263 element1 != EL_DRAGON && element2 != EL_DRAGON &&
5264 element1 != EL_FLAMES && element2 != EL_FLAMES)
5267 ResetGfxAnimation(x, y);
5268 GfxAction[x][y] = ACTION_ATTACKING;
5271 if (IS_PLAYER(x, y))
5272 DrawPlayerField(x, y);
5274 DrawLevelField(x, y);
5276 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5278 MovDelay[x][y] = 50;
5280 Feld[newx][newy] = EL_FLAMES;
5281 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5282 Feld[newx1][newy1] = EL_FLAMES;
5283 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5284 Feld[newx2][newy2] = EL_FLAMES;
5290 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5291 Feld[newx][newy] == EL_DIAMOND)
5293 if (IS_MOVING(newx, newy))
5294 RemoveMovingField(newx, newy);
5297 Feld[newx][newy] = EL_EMPTY;
5298 DrawLevelField(newx, newy);
5301 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5303 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5304 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5306 if (AmoebaNr[newx][newy])
5308 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5309 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5310 Feld[newx][newy] == EL_BD_AMOEBA)
5311 AmoebaCnt[AmoebaNr[newx][newy]]--;
5316 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
5318 if (IS_MOVING(newx, newy))
5321 RemoveMovingField(newx, newy);
5325 Feld[newx][newy] = EL_EMPTY;
5326 DrawLevelField(newx, newy);
5329 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5331 else if ((element == EL_PACMAN || element == EL_MOLE)
5332 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5334 if (AmoebaNr[newx][newy])
5336 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5337 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5338 Feld[newx][newy] == EL_BD_AMOEBA)
5339 AmoebaCnt[AmoebaNr[newx][newy]]--;
5342 if (element == EL_MOLE)
5344 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
5345 PlayLevelSound(x, y, SND_MOLE_DIGGING);
5347 ResetGfxAnimation(x, y);
5348 GfxAction[x][y] = ACTION_DIGGING;
5349 DrawLevelField(x, y);
5351 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
5353 return; /* wait for shrinking amoeba */
5355 else /* element == EL_PACMAN */
5357 Feld[newx][newy] = EL_EMPTY;
5358 DrawLevelField(newx, newy);
5359 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
5362 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
5363 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
5364 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
5366 /* wait for shrinking amoeba to completely disappear */
5369 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5371 /* object was running against a wall */
5376 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
5377 DrawLevelElementAnimation(x, y, element);
5379 if (element == EL_BUG ||
5380 element == EL_SPACESHIP ||
5381 element == EL_SP_SNIKSNAK)
5382 DrawLevelField(x, y);
5383 else if (element == EL_MOLE)
5384 DrawLevelField(x, y);
5385 else if (element == EL_BD_BUTTERFLY ||
5386 element == EL_BD_FIREFLY)
5387 DrawLevelElementAnimationIfNeeded(x, y, element);
5388 else if (element == EL_SATELLITE)
5389 DrawLevelElementAnimationIfNeeded(x, y, element);
5390 else if (element == EL_SP_ELECTRON)
5391 DrawLevelElementAnimationIfNeeded(x, y, element);
5394 if (DONT_TOUCH(element))
5395 TestIfBadThingTouchesHero(x, y);
5398 PlayLevelSoundAction(x, y, ACTION_WAITING);
5404 InitMovingField(x, y, MovDir[x][y]);
5406 PlayLevelSoundAction(x, y, ACTION_MOVING);
5410 ContinueMoving(x, y);
5413 void ContinueMoving(int x, int y)
5415 int element = Feld[x][y];
5416 struct ElementInfo *ei = &element_info[element];
5417 int direction = MovDir[x][y];
5418 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5419 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5420 int newx = x + dx, newy = y + dy;
5422 int nextx = newx + dx, nexty = newy + dy;
5425 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
5426 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
5428 boolean pushed_by_player = Pushed[x][y];
5431 MovPos[x][y] += getElementMoveStepsize(x, y);
5434 if (pushed_by_player && IS_PLAYER(x, y))
5436 /* special case: moving object pushed by player */
5437 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5440 if (pushed_by_player) /* special case: moving object pushed by player */
5441 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5444 if (ABS(MovPos[x][y]) < TILEX)
5446 DrawLevelField(x, y);
5448 return; /* element is still moving */
5451 /* element reached destination field */
5453 Feld[x][y] = EL_EMPTY;
5454 Feld[newx][newy] = element;
5455 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
5457 if (element == EL_MOLE)
5459 Feld[x][y] = EL_SAND;
5461 DrawLevelFieldCrumbledSandNeighbours(x, y);
5463 else if (element == EL_QUICKSAND_FILLING)
5465 element = Feld[newx][newy] = get_next_element(element);
5466 Store[newx][newy] = Store[x][y];
5468 else if (element == EL_QUICKSAND_EMPTYING)
5470 Feld[x][y] = get_next_element(element);
5471 element = Feld[newx][newy] = Store[x][y];
5473 else if (element == EL_MAGIC_WALL_FILLING)
5475 element = Feld[newx][newy] = get_next_element(element);
5476 if (!game.magic_wall_active)
5477 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
5478 Store[newx][newy] = Store[x][y];
5480 else if (element == EL_MAGIC_WALL_EMPTYING)
5482 Feld[x][y] = get_next_element(element);
5483 if (!game.magic_wall_active)
5484 Feld[x][y] = EL_MAGIC_WALL_DEAD;
5485 element = Feld[newx][newy] = Store[x][y];
5487 else if (element == EL_BD_MAGIC_WALL_FILLING)
5489 element = Feld[newx][newy] = get_next_element(element);
5490 if (!game.magic_wall_active)
5491 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
5492 Store[newx][newy] = Store[x][y];
5494 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
5496 Feld[x][y] = get_next_element(element);
5497 if (!game.magic_wall_active)
5498 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5499 element = Feld[newx][newy] = Store[x][y];
5501 else if (element == EL_AMOEBA_DROPPING)
5503 Feld[x][y] = get_next_element(element);
5504 element = Feld[newx][newy] = Store[x][y];
5506 else if (element == EL_SOKOBAN_OBJECT)
5509 Feld[x][y] = Back[x][y];
5511 if (Back[newx][newy])
5512 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
5514 Back[x][y] = Back[newx][newy] = 0;
5516 else if (Store[x][y] == EL_ACID)
5518 element = Feld[newx][newy] = EL_ACID;
5522 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
5523 MovDelay[newx][newy] = 0;
5525 /* copy element change control values to new field */
5526 ChangeDelay[newx][newy] = ChangeDelay[x][y];
5527 ChangePage[newx][newy] = ChangePage[x][y];
5528 Changed[newx][newy] = Changed[x][y];
5529 ChangeEvent[newx][newy] = ChangeEvent[x][y];
5531 ChangeDelay[x][y] = 0;
5532 ChangePage[x][y] = -1;
5533 Changed[x][y] = CE_BITMASK_DEFAULT;
5534 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
5536 /* copy animation control values to new field */
5537 GfxFrame[newx][newy] = GfxFrame[x][y];
5538 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
5539 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
5540 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
5542 Pushed[x][y] = Pushed[newx][newy] = FALSE;
5544 ResetGfxAnimation(x, y); /* reset animation values for old field */
5547 /* some elements can leave other elements behind after moving */
5548 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
5549 ei->move_leave_element != EL_EMPTY &&
5550 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
5551 ei->can_leave_element_last))
5553 Feld[x][y] = ei->move_leave_element;
5554 InitField(x, y, FALSE);
5556 if (GFX_CRUMBLED(Feld[x][y]))
5557 DrawLevelFieldCrumbledSandNeighbours(x, y);
5560 ei->can_leave_element_last = ei->can_leave_element;
5561 ei->can_leave_element = FALSE;
5565 /* 2.1.1 (does not work correctly for spring) */
5566 if (!CAN_MOVE(element))
5567 MovDir[newx][newy] = 0;
5571 /* (does not work for falling objects that slide horizontally) */
5572 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
5573 MovDir[newx][newy] = 0;
5576 if (!CAN_MOVE(element) ||
5577 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
5578 MovDir[newx][newy] = 0;
5581 if (!CAN_MOVE(element) ||
5582 (CAN_FALL(element) && direction == MV_DOWN))
5583 GfxDir[x][y] = MovDir[newx][newy] = 0;
5588 DrawLevelField(x, y);
5589 DrawLevelField(newx, newy);
5591 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
5593 /* prevent pushed element from moving on in pushed direction */
5594 if (pushed_by_player && CAN_MOVE(element) &&
5595 element_info[element].move_pattern & MV_ANY_DIRECTION &&
5596 !(element_info[element].move_pattern & direction))
5597 TurnRound(newx, newy);
5600 /* prevent elements on conveyor belt from moving on in last direction */
5601 if (pushed_by_conveyor && CAN_FALL(element) &&
5602 direction & MV_HORIZONTAL)
5603 MovDir[newx][newy] = 0;
5606 if (!pushed_by_player)
5608 WasJustMoving[newx][newy] = 3;
5610 if (CAN_FALL(element) && direction == MV_DOWN)
5611 WasJustFalling[newx][newy] = 3;
5614 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
5616 TestIfBadThingTouchesHero(newx, newy);
5617 TestIfBadThingTouchesFriend(newx, newy);
5619 if (!IS_CUSTOM_ELEMENT(element))
5620 TestIfBadThingTouchesOtherBadThing(newx, newy);
5622 else if (element == EL_PENGUIN)
5623 TestIfFriendTouchesBadThing(newx, newy);
5625 if (CAN_FALL(element) && direction == MV_DOWN &&
5626 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
5630 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
5634 if (ChangePage[newx][newy] != -1) /* delayed change */
5635 ChangeElement(newx, newy, ChangePage[newx][newy]);
5640 TestIfElementHitsCustomElement(newx, newy, direction);
5644 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
5646 int hitting_element = Feld[newx][newy];
5648 /* !!! fix side (direction) orientation here and elsewhere !!! */
5649 CheckElementChangeSide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
5653 if (IN_LEV_FIELD(nextx, nexty))
5655 int opposite_direction = MV_DIR_OPPOSITE(direction);
5656 int hitting_side = direction;
5657 int touched_side = opposite_direction;
5658 int touched_element = MovingOrBlocked2Element(nextx, nexty);
5659 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
5660 MovDir[nextx][nexty] != direction ||
5661 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
5667 CheckElementChangeSide(nextx, nexty, touched_element,
5668 CE_HIT_BY_SOMETHING, opposite_direction);
5670 if (IS_CUSTOM_ELEMENT(hitting_element) &&
5671 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
5673 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
5675 struct ElementChangeInfo *change =
5676 &element_info[hitting_element].change_page[i];
5678 if (change->can_change &&
5679 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
5680 change->trigger_side & touched_side &&
5681 change->trigger_element == touched_element)
5683 CheckElementChangePage(newx, newy, hitting_element,
5684 CE_OTHER_IS_HITTING, i);
5690 if (IS_CUSTOM_ELEMENT(touched_element) &&
5691 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
5693 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
5695 struct ElementChangeInfo *change =
5696 &element_info[touched_element].change_page[i];
5698 if (change->can_change &&
5699 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
5700 change->trigger_side & hitting_side &&
5701 change->trigger_element == hitting_element)
5703 CheckElementChangePage(nextx, nexty, touched_element,
5704 CE_OTHER_GETS_HIT, i);
5715 TestIfPlayerTouchesCustomElement(newx, newy);
5716 TestIfElementTouchesCustomElement(newx, newy);
5719 int AmoebeNachbarNr(int ax, int ay)
5722 int element = Feld[ax][ay];
5724 static int xy[4][2] =
5732 for (i = 0; i < NUM_DIRECTIONS; i++)
5734 int x = ax + xy[i][0];
5735 int y = ay + xy[i][1];
5737 if (!IN_LEV_FIELD(x, y))
5740 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
5741 group_nr = AmoebaNr[x][y];
5747 void AmoebenVereinigen(int ax, int ay)
5749 int i, x, y, xx, yy;
5750 int new_group_nr = AmoebaNr[ax][ay];
5751 static int xy[4][2] =
5759 if (new_group_nr == 0)
5762 for (i = 0; i < NUM_DIRECTIONS; i++)
5767 if (!IN_LEV_FIELD(x, y))
5770 if ((Feld[x][y] == EL_AMOEBA_FULL ||
5771 Feld[x][y] == EL_BD_AMOEBA ||
5772 Feld[x][y] == EL_AMOEBA_DEAD) &&
5773 AmoebaNr[x][y] != new_group_nr)
5775 int old_group_nr = AmoebaNr[x][y];
5777 if (old_group_nr == 0)
5780 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
5781 AmoebaCnt[old_group_nr] = 0;
5782 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
5783 AmoebaCnt2[old_group_nr] = 0;
5785 for (yy = 0; yy < lev_fieldy; yy++)
5787 for (xx = 0; xx < lev_fieldx; xx++)
5789 if (AmoebaNr[xx][yy] == old_group_nr)
5790 AmoebaNr[xx][yy] = new_group_nr;
5797 void AmoebeUmwandeln(int ax, int ay)
5801 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
5803 int group_nr = AmoebaNr[ax][ay];
5808 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
5809 printf("AmoebeUmwandeln(): This should never happen!\n");
5814 for (y = 0; y < lev_fieldy; y++)
5816 for (x = 0; x < lev_fieldx; x++)
5818 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
5821 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
5825 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
5826 SND_AMOEBA_TURNING_TO_GEM :
5827 SND_AMOEBA_TURNING_TO_ROCK));
5832 static int xy[4][2] =
5840 for (i = 0; i < NUM_DIRECTIONS; i++)
5845 if (!IN_LEV_FIELD(x, y))
5848 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
5850 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
5851 SND_AMOEBA_TURNING_TO_GEM :
5852 SND_AMOEBA_TURNING_TO_ROCK));
5859 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
5862 int group_nr = AmoebaNr[ax][ay];
5863 boolean done = FALSE;
5868 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
5869 printf("AmoebeUmwandelnBD(): This should never happen!\n");
5874 for (y = 0; y < lev_fieldy; y++)
5876 for (x = 0; x < lev_fieldx; x++)
5878 if (AmoebaNr[x][y] == group_nr &&
5879 (Feld[x][y] == EL_AMOEBA_DEAD ||
5880 Feld[x][y] == EL_BD_AMOEBA ||
5881 Feld[x][y] == EL_AMOEBA_GROWING))
5884 Feld[x][y] = new_element;
5885 InitField(x, y, FALSE);
5886 DrawLevelField(x, y);
5893 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
5894 SND_BD_AMOEBA_TURNING_TO_ROCK :
5895 SND_BD_AMOEBA_TURNING_TO_GEM));
5898 void AmoebeWaechst(int x, int y)
5900 static unsigned long sound_delay = 0;
5901 static unsigned long sound_delay_value = 0;
5903 if (!MovDelay[x][y]) /* start new growing cycle */
5907 if (DelayReached(&sound_delay, sound_delay_value))
5910 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
5912 if (Store[x][y] == EL_BD_AMOEBA)
5913 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
5915 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
5917 sound_delay_value = 30;
5921 if (MovDelay[x][y]) /* wait some time before growing bigger */
5924 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5926 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
5927 6 - MovDelay[x][y]);
5929 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
5932 if (!MovDelay[x][y])
5934 Feld[x][y] = Store[x][y];
5936 DrawLevelField(x, y);
5941 void AmoebaDisappearing(int x, int y)
5943 static unsigned long sound_delay = 0;
5944 static unsigned long sound_delay_value = 0;
5946 if (!MovDelay[x][y]) /* start new shrinking cycle */
5950 if (DelayReached(&sound_delay, sound_delay_value))
5951 sound_delay_value = 30;
5954 if (MovDelay[x][y]) /* wait some time before shrinking */
5957 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5959 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
5960 6 - MovDelay[x][y]);
5962 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
5965 if (!MovDelay[x][y])
5967 Feld[x][y] = EL_EMPTY;
5968 DrawLevelField(x, y);
5970 /* don't let mole enter this field in this cycle;
5971 (give priority to objects falling to this field from above) */
5977 void AmoebeAbleger(int ax, int ay)
5980 int element = Feld[ax][ay];
5981 int graphic = el2img(element);
5982 int newax = ax, neway = ay;
5983 static int xy[4][2] =
5991 if (!level.amoeba_speed)
5993 Feld[ax][ay] = EL_AMOEBA_DEAD;
5994 DrawLevelField(ax, ay);
5998 if (IS_ANIMATED(graphic))
5999 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6001 if (!MovDelay[ax][ay]) /* start making new amoeba field */
6002 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6004 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
6007 if (MovDelay[ax][ay])
6011 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
6014 int x = ax + xy[start][0];
6015 int y = ay + xy[start][1];
6017 if (!IN_LEV_FIELD(x, y))
6020 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6021 if (IS_FREE(x, y) ||
6022 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6028 if (newax == ax && neway == ay)
6031 else /* normal or "filled" (BD style) amoeba */
6034 boolean waiting_for_player = FALSE;
6036 for (i = 0; i < NUM_DIRECTIONS; i++)
6038 int j = (start + i) % 4;
6039 int x = ax + xy[j][0];
6040 int y = ay + xy[j][1];
6042 if (!IN_LEV_FIELD(x, y))
6045 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6046 if (IS_FREE(x, y) ||
6047 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6053 else if (IS_PLAYER(x, y))
6054 waiting_for_player = TRUE;
6057 if (newax == ax && neway == ay) /* amoeba cannot grow */
6059 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
6061 Feld[ax][ay] = EL_AMOEBA_DEAD;
6062 DrawLevelField(ax, ay);
6063 AmoebaCnt[AmoebaNr[ax][ay]]--;
6065 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
6067 if (element == EL_AMOEBA_FULL)
6068 AmoebeUmwandeln(ax, ay);
6069 else if (element == EL_BD_AMOEBA)
6070 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6075 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6077 /* amoeba gets larger by growing in some direction */
6079 int new_group_nr = AmoebaNr[ax][ay];
6082 if (new_group_nr == 0)
6084 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6085 printf("AmoebeAbleger(): This should never happen!\n");
6090 AmoebaNr[newax][neway] = new_group_nr;
6091 AmoebaCnt[new_group_nr]++;
6092 AmoebaCnt2[new_group_nr]++;
6094 /* if amoeba touches other amoeba(s) after growing, unify them */
6095 AmoebenVereinigen(newax, neway);
6097 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6099 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6105 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
6106 (neway == lev_fieldy - 1 && newax != ax))
6108 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
6109 Store[newax][neway] = element;
6111 else if (neway == ay)
6113 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
6115 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6117 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
6122 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
6123 Feld[ax][ay] = EL_AMOEBA_DROPPING;
6124 Store[ax][ay] = EL_AMOEBA_DROP;
6125 ContinueMoving(ax, ay);
6129 DrawLevelField(newax, neway);
6132 void Life(int ax, int ay)
6135 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
6137 int element = Feld[ax][ay];
6138 int graphic = el2img(element);
6139 boolean changed = FALSE;
6141 if (IS_ANIMATED(graphic))
6142 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6147 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
6148 MovDelay[ax][ay] = life_time;
6150 if (MovDelay[ax][ay]) /* wait some time before next cycle */
6153 if (MovDelay[ax][ay])
6157 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
6159 int xx = ax+x1, yy = ay+y1;
6162 if (!IN_LEV_FIELD(xx, yy))
6165 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
6167 int x = xx+x2, y = yy+y2;
6169 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
6172 if (((Feld[x][y] == element ||
6173 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
6175 (IS_FREE(x, y) && Stop[x][y]))
6179 if (xx == ax && yy == ay) /* field in the middle */
6181 if (nachbarn < life[0] || nachbarn > life[1])
6183 Feld[xx][yy] = EL_EMPTY;
6185 DrawLevelField(xx, yy);
6186 Stop[xx][yy] = TRUE;
6190 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6191 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
6192 { /* free border field */
6193 if (nachbarn >= life[2] && nachbarn <= life[3])
6195 Feld[xx][yy] = element;
6196 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6198 DrawLevelField(xx, yy);
6199 Stop[xx][yy] = TRUE;
6206 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
6207 SND_GAME_OF_LIFE_GROWING);
6210 static void InitRobotWheel(int x, int y)
6212 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6215 static void RunRobotWheel(int x, int y)
6217 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
6220 static void StopRobotWheel(int x, int y)
6222 if (ZX == x && ZY == y)
6226 static void InitTimegateWheel(int x, int y)
6228 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6231 static void RunTimegateWheel(int x, int y)
6233 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
6236 void CheckExit(int x, int y)
6238 if (local_player->gems_still_needed > 0 ||
6239 local_player->sokobanfields_still_needed > 0 ||
6240 local_player->lights_still_needed > 0)
6242 int element = Feld[x][y];
6243 int graphic = el2img(element);
6245 if (IS_ANIMATED(graphic))
6246 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6251 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6254 Feld[x][y] = EL_EXIT_OPENING;
6256 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
6259 void CheckExitSP(int x, int y)
6261 if (local_player->gems_still_needed > 0)
6263 int element = Feld[x][y];
6264 int graphic = el2img(element);
6266 if (IS_ANIMATED(graphic))
6267 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6272 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6275 Feld[x][y] = EL_SP_EXIT_OPENING;
6277 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
6280 static void CloseAllOpenTimegates()
6284 for (y = 0; y < lev_fieldy; y++)
6286 for (x = 0; x < lev_fieldx; x++)
6288 int element = Feld[x][y];
6290 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
6292 Feld[x][y] = EL_TIMEGATE_CLOSING;
6294 PlayLevelSoundAction(x, y, ACTION_CLOSING);
6296 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
6303 void EdelsteinFunkeln(int x, int y)
6305 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
6308 if (Feld[x][y] == EL_BD_DIAMOND)
6311 if (MovDelay[x][y] == 0) /* next animation frame */
6312 MovDelay[x][y] = 11 * !SimpleRND(500);
6314 if (MovDelay[x][y] != 0) /* wait some time before next frame */
6318 if (setup.direct_draw && MovDelay[x][y])
6319 SetDrawtoField(DRAW_BUFFERED);
6321 DrawLevelElementAnimation(x, y, Feld[x][y]);
6323 if (MovDelay[x][y] != 0)
6325 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
6326 10 - MovDelay[x][y]);
6328 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
6330 if (setup.direct_draw)
6334 dest_x = FX + SCREENX(x) * TILEX;
6335 dest_y = FY + SCREENY(y) * TILEY;
6337 BlitBitmap(drawto_field, window,
6338 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
6339 SetDrawtoField(DRAW_DIRECT);
6345 void MauerWaechst(int x, int y)
6349 if (!MovDelay[x][y]) /* next animation frame */
6350 MovDelay[x][y] = 3 * delay;
6352 if (MovDelay[x][y]) /* wait some time before next frame */
6356 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6358 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
6359 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
6361 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6364 if (!MovDelay[x][y])
6366 if (MovDir[x][y] == MV_LEFT)
6368 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
6369 DrawLevelField(x - 1, y);
6371 else if (MovDir[x][y] == MV_RIGHT)
6373 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
6374 DrawLevelField(x + 1, y);
6376 else if (MovDir[x][y] == MV_UP)
6378 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
6379 DrawLevelField(x, y - 1);
6383 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
6384 DrawLevelField(x, y + 1);
6387 Feld[x][y] = Store[x][y];
6389 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
6390 DrawLevelField(x, y);
6395 void MauerAbleger(int ax, int ay)
6397 int element = Feld[ax][ay];
6398 int graphic = el2img(element);
6399 boolean oben_frei = FALSE, unten_frei = FALSE;
6400 boolean links_frei = FALSE, rechts_frei = FALSE;
6401 boolean oben_massiv = FALSE, unten_massiv = FALSE;
6402 boolean links_massiv = FALSE, rechts_massiv = FALSE;
6403 boolean new_wall = FALSE;
6405 if (IS_ANIMATED(graphic))
6406 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6408 if (!MovDelay[ax][ay]) /* start building new wall */
6409 MovDelay[ax][ay] = 6;
6411 if (MovDelay[ax][ay]) /* wait some time before building new wall */
6414 if (MovDelay[ax][ay])
6418 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
6420 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
6422 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
6424 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
6427 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
6428 element == EL_EXPANDABLE_WALL_ANY)
6432 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
6433 Store[ax][ay-1] = element;
6434 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
6435 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
6436 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
6437 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
6442 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
6443 Store[ax][ay+1] = element;
6444 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
6445 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
6446 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
6447 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
6452 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6453 element == EL_EXPANDABLE_WALL_ANY ||
6454 element == EL_EXPANDABLE_WALL)
6458 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
6459 Store[ax-1][ay] = element;
6460 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
6461 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
6462 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
6463 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
6469 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
6470 Store[ax+1][ay] = element;
6471 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
6472 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
6473 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
6474 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
6479 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
6480 DrawLevelField(ax, ay);
6482 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
6484 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
6485 unten_massiv = TRUE;
6486 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
6487 links_massiv = TRUE;
6488 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
6489 rechts_massiv = TRUE;
6491 if (((oben_massiv && unten_massiv) ||
6492 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6493 element == EL_EXPANDABLE_WALL) &&
6494 ((links_massiv && rechts_massiv) ||
6495 element == EL_EXPANDABLE_WALL_VERTICAL))
6496 Feld[ax][ay] = EL_WALL;
6500 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
6502 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
6506 void CheckForDragon(int x, int y)
6509 boolean dragon_found = FALSE;
6510 static int xy[4][2] =
6518 for (i = 0; i < NUM_DIRECTIONS; i++)
6520 for (j = 0; j < 4; j++)
6522 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
6524 if (IN_LEV_FIELD(xx, yy) &&
6525 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
6527 if (Feld[xx][yy] == EL_DRAGON)
6528 dragon_found = TRUE;
6537 for (i = 0; i < NUM_DIRECTIONS; i++)
6539 for (j = 0; j < 3; j++)
6541 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
6543 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
6545 Feld[xx][yy] = EL_EMPTY;
6546 DrawLevelField(xx, yy);
6555 static void InitBuggyBase(int x, int y)
6557 int element = Feld[x][y];
6558 int activating_delay = FRAMES_PER_SECOND / 4;
6561 (element == EL_SP_BUGGY_BASE ?
6562 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
6563 element == EL_SP_BUGGY_BASE_ACTIVATING ?
6565 element == EL_SP_BUGGY_BASE_ACTIVE ?
6566 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
6569 static void WarnBuggyBase(int x, int y)
6572 static int xy[4][2] =
6580 for (i = 0; i < NUM_DIRECTIONS; i++)
6582 int xx = x + xy[i][0], yy = y + xy[i][1];
6584 if (IS_PLAYER(xx, yy))
6586 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
6593 static void InitTrap(int x, int y)
6595 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
6598 static void ActivateTrap(int x, int y)
6600 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
6603 static void ChangeActiveTrap(int x, int y)
6605 int graphic = IMG_TRAP_ACTIVE;
6607 /* if new animation frame was drawn, correct crumbled sand border */
6608 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
6609 DrawLevelFieldCrumbledSand(x, y);
6612 static void ChangeElementNowExt(int x, int y, int target_element)
6614 int previous_move_direction = MovDir[x][y];
6616 /* check if element under player changes from accessible to unaccessible
6617 (needed for special case of dropping element which then changes) */
6618 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
6619 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
6626 Feld[x][y] = target_element;
6628 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6630 ResetGfxAnimation(x, y);
6631 ResetRandomAnimationValue(x, y);
6633 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6634 MovDir[x][y] = previous_move_direction;
6637 InitField_WithBug1(x, y, FALSE);
6639 InitField(x, y, FALSE);
6640 if (CAN_MOVE(Feld[x][y]))
6644 DrawLevelField(x, y);
6646 if (GFX_CRUMBLED(Feld[x][y]))
6647 DrawLevelFieldCrumbledSandNeighbours(x, y);
6649 TestIfBadThingTouchesHero(x, y);
6650 TestIfPlayerTouchesCustomElement(x, y);
6651 TestIfElementTouchesCustomElement(x, y);
6653 if (ELEM_IS_PLAYER(target_element))
6654 RelocatePlayer(x, y, target_element);
6657 static boolean ChangeElementNow(int x, int y, int element, int page)
6659 struct ElementChangeInfo *change = &element_info[element].change_page[page];
6661 /* always use default change event to prevent running into a loop */
6662 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
6663 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
6665 /* do not change already changed elements with same change event */
6667 if (Changed[x][y] & ChangeEvent[x][y])
6674 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6676 CheckTriggeredElementChangePage(x,y, Feld[x][y], CE_OTHER_IS_CHANGING, page);
6678 if (change->explode)
6685 if (change->use_content)
6687 boolean complete_change = TRUE;
6688 boolean can_change[3][3];
6691 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6693 boolean half_destructible;
6694 int ex = x + xx - 1;
6695 int ey = y + yy - 1;
6698 can_change[xx][yy] = TRUE;
6700 if (ex == x && ey == y) /* do not check changing element itself */
6703 if (change->content[xx][yy] == EL_EMPTY_SPACE)
6705 can_change[xx][yy] = FALSE; /* do not change empty borders */
6710 if (!IN_LEV_FIELD(ex, ey))
6712 can_change[xx][yy] = FALSE;
6713 complete_change = FALSE;
6720 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6721 e = MovingOrBlocked2Element(ex, ey);
6723 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
6725 if ((change->power <= CP_NON_DESTRUCTIVE && !IS_FREE(ex, ey)) ||
6726 (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
6727 (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
6729 can_change[xx][yy] = FALSE;
6730 complete_change = FALSE;
6734 if (!change->only_complete || complete_change)
6736 boolean something_has_changed = FALSE;
6738 if (change->only_complete && change->use_random_change &&
6739 RND(100) < change->random)
6742 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6744 int ex = x + xx - 1;
6745 int ey = y + yy - 1;
6747 if (can_change[xx][yy] && (!change->use_random_change ||
6748 RND(100) < change->random))
6750 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6751 RemoveMovingField(ex, ey);
6753 ChangeEvent[ex][ey] = ChangeEvent[x][y];
6755 ChangeElementNowExt(ex, ey, change->content[xx][yy]);
6757 something_has_changed = TRUE;
6759 /* for symmetry reasons, freeze newly created border elements */
6760 if (ex != x || ey != y)
6761 Stop[ex][ey] = TRUE; /* no more moving in this frame */
6765 if (something_has_changed)
6766 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6771 ChangeElementNowExt(x, y, change->target_element);
6773 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6779 static void ChangeElement(int x, int y, int page)
6781 int element = MovingOrBlocked2Element(x, y);
6782 struct ElementInfo *ei = &element_info[element];
6783 struct ElementChangeInfo *change = &ei->change_page[page];
6787 if (!CAN_CHANGE(element))
6790 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
6791 x, y, element, element_info[element].token_name);
6792 printf("ChangeElement(): This should never happen!\n");
6798 if (ChangeDelay[x][y] == 0) /* initialize element change */
6800 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
6801 RND(change->delay_random * change->delay_frames)) + 1;
6803 ResetGfxAnimation(x, y);
6804 ResetRandomAnimationValue(x, y);
6806 if (change->pre_change_function)
6807 change->pre_change_function(x, y);
6810 ChangeDelay[x][y]--;
6812 if (ChangeDelay[x][y] != 0) /* continue element change */
6814 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6816 if (IS_ANIMATED(graphic))
6817 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6819 if (change->change_function)
6820 change->change_function(x, y);
6822 else /* finish element change */
6824 if (ChangePage[x][y] != -1) /* remember page from delayed change */
6826 page = ChangePage[x][y];
6827 ChangePage[x][y] = -1;
6831 if (IS_MOVING(x, y) && !change->explode)
6833 if (IS_MOVING(x, y)) /* never change a running system ;-) */
6836 ChangeDelay[x][y] = 1; /* try change after next move step */
6837 ChangePage[x][y] = page; /* remember page to use for change */
6842 if (ChangeElementNow(x, y, element, page))
6844 if (change->post_change_function)
6845 change->post_change_function(x, y);
6850 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
6851 int trigger_element,
6859 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
6862 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6864 int element = EL_CUSTOM_START + i;
6866 boolean change_element = FALSE;
6869 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6872 for (j = 0; j < element_info[element].num_change_pages; j++)
6874 struct ElementChangeInfo *change = &element_info[element].change_page[j];
6876 if (change->can_change &&
6877 change->events & CH_EVENT_BIT(trigger_event) &&
6878 change->trigger_side & trigger_side &&
6879 change->trigger_player & trigger_player &&
6880 change->trigger_page & (1 << trigger_page) &&
6881 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
6884 if (!(change->events & CH_EVENT_BIT(trigger_event)))
6885 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
6886 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
6889 change_element = TRUE;
6896 if (!change_element)
6899 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6902 if (x == lx && y == ly) /* do not change trigger element itself */
6906 if (Feld[x][y] == element)
6908 ChangeDelay[x][y] = 1;
6909 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6910 ChangeElement(x, y, page);
6918 static boolean CheckElementChangeExt(int x, int y,
6925 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6928 if (Feld[x][y] == EL_BLOCKED)
6930 Blocked2Moving(x, y, &x, &y);
6931 element = Feld[x][y];
6935 if (trigger_page < 0)
6937 boolean change_element = FALSE;
6940 for (i = 0; i < element_info[element].num_change_pages; i++)
6942 struct ElementChangeInfo *change = &element_info[element].change_page[i];
6944 if (change->can_change &&
6945 change->events & CH_EVENT_BIT(trigger_event) &&
6946 change->trigger_side & trigger_side &&
6947 change->trigger_player & trigger_player)
6949 change_element = TRUE;
6956 if (!change_element)
6962 /* !!! this check misses pages with same event, but different side !!! */
6964 if (trigger_page < 0)
6965 trigger_page = element_info[element].event_page_nr[trigger_event];
6967 if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
6971 ChangeDelay[x][y] = 1;
6972 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6973 ChangeElement(x, y, trigger_page);
6978 static void PlayPlayerSound(struct PlayerInfo *player)
6980 int jx = player->jx, jy = player->jy;
6981 int element = player->element_nr;
6982 int last_action = player->last_action_waiting;
6983 int action = player->action_waiting;
6985 if (player->is_waiting)
6987 if (action != last_action)
6988 PlayLevelSoundElementAction(jx, jy, element, action);
6990 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
6994 if (action != last_action)
6995 StopSound(element_info[element].sound[last_action]);
6997 if (last_action == ACTION_SLEEPING)
6998 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
7002 static void PlayAllPlayersSound()
7006 for (i = 0; i < MAX_PLAYERS; i++)
7007 if (stored_player[i].active)
7008 PlayPlayerSound(&stored_player[i]);
7011 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
7013 boolean last_waiting = player->is_waiting;
7014 int move_dir = player->MovDir;
7016 player->last_action_waiting = player->action_waiting;
7020 if (!last_waiting) /* not waiting -> waiting */
7022 player->is_waiting = TRUE;
7024 player->frame_counter_bored =
7026 game.player_boring_delay_fixed +
7027 SimpleRND(game.player_boring_delay_random);
7028 player->frame_counter_sleeping =
7030 game.player_sleeping_delay_fixed +
7031 SimpleRND(game.player_sleeping_delay_random);
7033 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
7036 if (game.player_sleeping_delay_fixed +
7037 game.player_sleeping_delay_random > 0 &&
7038 player->anim_delay_counter == 0 &&
7039 player->post_delay_counter == 0 &&
7040 FrameCounter >= player->frame_counter_sleeping)
7041 player->is_sleeping = TRUE;
7042 else if (game.player_boring_delay_fixed +
7043 game.player_boring_delay_random > 0 &&
7044 FrameCounter >= player->frame_counter_bored)
7045 player->is_bored = TRUE;
7047 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
7048 player->is_bored ? ACTION_BORING :
7051 if (player->is_sleeping)
7053 if (player->num_special_action_sleeping > 0)
7055 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7057 int last_special_action = player->special_action_sleeping;
7058 int num_special_action = player->num_special_action_sleeping;
7059 int special_action =
7060 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
7061 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
7062 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
7063 last_special_action + 1 : ACTION_SLEEPING);
7064 int special_graphic =
7065 el_act_dir2img(player->element_nr, special_action, move_dir);
7067 player->anim_delay_counter =
7068 graphic_info[special_graphic].anim_delay_fixed +
7069 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7070 player->post_delay_counter =
7071 graphic_info[special_graphic].post_delay_fixed +
7072 SimpleRND(graphic_info[special_graphic].post_delay_random);
7074 player->special_action_sleeping = special_action;
7077 if (player->anim_delay_counter > 0)
7079 player->action_waiting = player->special_action_sleeping;
7080 player->anim_delay_counter--;
7082 else if (player->post_delay_counter > 0)
7084 player->post_delay_counter--;
7088 else if (player->is_bored)
7090 if (player->num_special_action_bored > 0)
7092 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7094 int special_action =
7095 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
7096 int special_graphic =
7097 el_act_dir2img(player->element_nr, special_action, move_dir);
7099 player->anim_delay_counter =
7100 graphic_info[special_graphic].anim_delay_fixed +
7101 SimpleRND(graphic_info[special_graphic].anim_delay_random);
7102 player->post_delay_counter =
7103 graphic_info[special_graphic].post_delay_fixed +
7104 SimpleRND(graphic_info[special_graphic].post_delay_random);
7106 player->special_action_bored = special_action;
7109 if (player->anim_delay_counter > 0)
7111 player->action_waiting = player->special_action_bored;
7112 player->anim_delay_counter--;
7114 else if (player->post_delay_counter > 0)
7116 player->post_delay_counter--;
7121 else if (last_waiting) /* waiting -> not waiting */
7123 player->is_waiting = FALSE;
7124 player->is_bored = FALSE;
7125 player->is_sleeping = FALSE;
7127 player->frame_counter_bored = -1;
7128 player->frame_counter_sleeping = -1;
7130 player->anim_delay_counter = 0;
7131 player->post_delay_counter = 0;
7133 player->action_waiting = ACTION_DEFAULT;
7135 player->special_action_bored = ACTION_DEFAULT;
7136 player->special_action_sleeping = ACTION_DEFAULT;
7141 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
7144 static byte stored_player_action[MAX_PLAYERS];
7145 static int num_stored_actions = 0;
7147 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7148 int left = player_action & JOY_LEFT;
7149 int right = player_action & JOY_RIGHT;
7150 int up = player_action & JOY_UP;
7151 int down = player_action & JOY_DOWN;
7152 int button1 = player_action & JOY_BUTTON_1;
7153 int button2 = player_action & JOY_BUTTON_2;
7154 int dx = (left ? -1 : right ? 1 : 0);
7155 int dy = (up ? -1 : down ? 1 : 0);
7158 stored_player_action[player->index_nr] = 0;
7159 num_stored_actions++;
7163 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
7166 if (!player->active || tape.pausing)
7170 printf("::: [%d %d %d %d] [%d %d]\n",
7171 left, right, up, down, button1, button2);
7177 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
7181 snapped = SnapField(player, dx, dy);
7185 dropped = DropElement(player);
7187 moved = MovePlayer(player, dx, dy);
7190 if (tape.single_step && tape.recording && !tape.pausing)
7192 if (button1 || (dropped && !moved))
7194 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7195 SnapField(player, 0, 0); /* stop snapping */
7199 SetPlayerWaiting(player, FALSE);
7202 return player_action;
7204 stored_player_action[player->index_nr] = player_action;
7210 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
7213 /* no actions for this player (no input at player's configured device) */
7215 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7216 SnapField(player, 0, 0);
7217 CheckGravityMovement(player);
7219 if (player->MovPos == 0)
7220 SetPlayerWaiting(player, TRUE);
7222 if (player->MovPos == 0) /* needed for tape.playing */
7223 player->is_moving = FALSE;
7225 player->is_dropping = FALSE;
7231 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
7233 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
7235 TapeRecordAction(stored_player_action);
7236 num_stored_actions = 0;
7243 static void PlayerActions(struct PlayerInfo *player, byte player_action)
7245 static byte stored_player_action[MAX_PLAYERS];
7246 static int num_stored_actions = 0;
7247 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7248 int left = player_action & JOY_LEFT;
7249 int right = player_action & JOY_RIGHT;
7250 int up = player_action & JOY_UP;
7251 int down = player_action & JOY_DOWN;
7252 int button1 = player_action & JOY_BUTTON_1;
7253 int button2 = player_action & JOY_BUTTON_2;
7254 int dx = (left ? -1 : right ? 1 : 0);
7255 int dy = (up ? -1 : down ? 1 : 0);
7257 stored_player_action[player->index_nr] = 0;
7258 num_stored_actions++;
7260 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
7262 if (!player->active || tape.pausing)
7267 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
7270 snapped = SnapField(player, dx, dy);
7274 dropped = DropElement(player);
7276 moved = MovePlayer(player, dx, dy);
7279 if (tape.single_step && tape.recording && !tape.pausing)
7281 if (button1 || (dropped && !moved))
7283 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7284 SnapField(player, 0, 0); /* stop snapping */
7288 stored_player_action[player->index_nr] = player_action;
7292 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
7294 /* no actions for this player (no input at player's configured device) */
7296 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7297 SnapField(player, 0, 0);
7298 CheckGravityMovement(player);
7300 if (player->MovPos == 0)
7301 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
7303 if (player->MovPos == 0) /* needed for tape.playing */
7304 player->is_moving = FALSE;
7307 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
7309 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
7311 TapeRecordAction(stored_player_action);
7312 num_stored_actions = 0;
7319 static unsigned long action_delay = 0;
7320 unsigned long action_delay_value;
7321 int magic_wall_x = 0, magic_wall_y = 0;
7322 int i, x, y, element, graphic;
7323 byte *recorded_player_action;
7324 byte summarized_player_action = 0;
7326 byte tape_action[MAX_PLAYERS];
7329 if (game_status != GAME_MODE_PLAYING)
7332 action_delay_value =
7333 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
7335 if (tape.playing && tape.index_search && !tape.pausing)
7336 action_delay_value = 0;
7338 /* ---------- main game synchronization point ---------- */
7340 WaitUntilDelayReached(&action_delay, action_delay_value);
7342 if (network_playing && !network_player_action_received)
7346 printf("DEBUG: try to get network player actions in time\n");
7350 #if defined(PLATFORM_UNIX)
7351 /* last chance to get network player actions without main loop delay */
7355 if (game_status != GAME_MODE_PLAYING)
7358 if (!network_player_action_received)
7362 printf("DEBUG: failed to get network player actions in time\n");
7373 printf("::: getting new tape action [%d]\n", FrameCounter);
7376 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
7378 for (i = 0; i < MAX_PLAYERS; i++)
7380 summarized_player_action |= stored_player[i].action;
7382 if (!network_playing)
7383 stored_player[i].effective_action = stored_player[i].action;
7386 #if defined(PLATFORM_UNIX)
7387 if (network_playing)
7388 SendToServer_MovePlayer(summarized_player_action);
7391 if (!options.network && !setup.team_mode)
7392 local_player->effective_action = summarized_player_action;
7394 for (i = 0; i < MAX_PLAYERS; i++)
7396 int actual_player_action = stored_player[i].effective_action;
7398 if (stored_player[i].programmed_action)
7399 actual_player_action = stored_player[i].programmed_action;
7401 if (recorded_player_action)
7402 actual_player_action = recorded_player_action[i];
7404 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
7406 if (tape.recording && tape_action[i] && !tape.player_participates[i])
7407 tape.player_participates[i] = TRUE; /* player just appeared from CE */
7409 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
7414 TapeRecordAction(tape_action);
7417 network_player_action_received = FALSE;
7419 ScrollScreen(NULL, SCROLL_GO_ON);
7425 for (i = 0; i < MAX_PLAYERS; i++)
7426 stored_player[i].Frame++;
7430 /* for downwards compatibility, the following code emulates a fixed bug that
7431 occured when pushing elements (causing elements that just made their last
7432 pushing step to already (if possible) make their first falling step in the
7433 same game frame, which is bad); this code is also needed to use the famous
7434 "spring push bug" which is used in older levels and might be wanted to be
7435 used also in newer levels, but in this case the buggy pushing code is only
7436 affecting the "spring" element and no other elements */
7439 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
7441 if (game.engine_version < VERSION_IDENT(2,2,0,7))
7444 for (i = 0; i < MAX_PLAYERS; i++)
7446 struct PlayerInfo *player = &stored_player[i];
7451 if (player->active && player->is_pushing && player->is_moving &&
7453 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
7454 Feld[x][y] == EL_SPRING))
7456 if (player->active && player->is_pushing && player->is_moving &&
7460 ContinueMoving(x, y);
7462 /* continue moving after pushing (this is actually a bug) */
7463 if (!IS_MOVING(x, y))
7472 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7474 Changed[x][y] = CE_BITMASK_DEFAULT;
7475 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
7478 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
7480 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
7481 printf("GameActions(): This should never happen!\n");
7483 ChangePage[x][y] = -1;
7488 if (WasJustMoving[x][y] > 0)
7489 WasJustMoving[x][y]--;
7490 if (WasJustFalling[x][y] > 0)
7491 WasJustFalling[x][y]--;
7496 /* reset finished pushing action (not done in ContinueMoving() to allow
7497 continous pushing animation for elements with zero push delay) */
7498 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
7500 ResetGfxAnimation(x, y);
7501 DrawLevelField(x, y);
7506 if (IS_BLOCKED(x, y))
7510 Blocked2Moving(x, y, &oldx, &oldy);
7511 if (!IS_MOVING(oldx, oldy))
7513 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
7514 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
7515 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
7516 printf("GameActions(): This should never happen!\n");
7522 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7524 element = Feld[x][y];
7526 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7528 graphic = el2img(element);
7534 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
7536 element = graphic = 0;
7540 if (graphic_info[graphic].anim_global_sync)
7541 GfxFrame[x][y] = FrameCounter;
7543 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
7544 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
7545 ResetRandomAnimationValue(x, y);
7547 SetRandomAnimationValue(x, y);
7550 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
7553 if (IS_INACTIVE(element))
7555 if (IS_ANIMATED(graphic))
7556 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7562 /* this may take place after moving, so 'element' may have changed */
7564 if (IS_CHANGING(x, y))
7566 if (IS_CHANGING(x, y) &&
7567 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
7571 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
7572 element_info[element].event_page_nr[CE_DELAY]);
7574 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
7577 element = Feld[x][y];
7578 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7582 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
7587 element = Feld[x][y];
7588 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7590 if (element == EL_MOLE)
7591 printf("::: %d, %d, %d [%d]\n",
7592 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
7596 if (element == EL_YAMYAM)
7597 printf("::: %d, %d, %d\n",
7598 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
7602 if (IS_ANIMATED(graphic) &&
7606 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7609 if (element == EL_BUG)
7610 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7614 if (element == EL_MOLE)
7615 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7619 if (IS_GEM(element) || element == EL_SP_INFOTRON)
7620 EdelsteinFunkeln(x, y);
7622 else if ((element == EL_ACID ||
7623 element == EL_EXIT_OPEN ||
7624 element == EL_SP_EXIT_OPEN ||
7625 element == EL_SP_TERMINAL ||
7626 element == EL_SP_TERMINAL_ACTIVE ||
7627 element == EL_EXTRA_TIME ||
7628 element == EL_SHIELD_NORMAL ||
7629 element == EL_SHIELD_DEADLY) &&
7630 IS_ANIMATED(graphic))
7631 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7632 else if (IS_MOVING(x, y))
7633 ContinueMoving(x, y);
7634 else if (IS_ACTIVE_BOMB(element))
7635 CheckDynamite(x, y);
7637 else if (element == EL_EXPLOSION && !game.explosions_delayed)
7638 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7640 else if (element == EL_AMOEBA_GROWING)
7641 AmoebeWaechst(x, y);
7642 else if (element == EL_AMOEBA_SHRINKING)
7643 AmoebaDisappearing(x, y);
7645 #if !USE_NEW_AMOEBA_CODE
7646 else if (IS_AMOEBALIVE(element))
7647 AmoebeAbleger(x, y);
7650 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
7652 else if (element == EL_EXIT_CLOSED)
7654 else if (element == EL_SP_EXIT_CLOSED)
7656 else if (element == EL_EXPANDABLE_WALL_GROWING)
7658 else if (element == EL_EXPANDABLE_WALL ||
7659 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7660 element == EL_EXPANDABLE_WALL_VERTICAL ||
7661 element == EL_EXPANDABLE_WALL_ANY)
7663 else if (element == EL_FLAMES)
7664 CheckForDragon(x, y);
7666 else if (IS_AUTO_CHANGING(element))
7667 ChangeElement(x, y);
7669 else if (element == EL_EXPLOSION)
7670 ; /* drawing of correct explosion animation is handled separately */
7671 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
7672 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7675 /* this may take place after moving, so 'element' may have changed */
7676 if (IS_AUTO_CHANGING(Feld[x][y]))
7677 ChangeElement(x, y);
7680 if (IS_BELT_ACTIVE(element))
7681 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
7683 if (game.magic_wall_active)
7685 int jx = local_player->jx, jy = local_player->jy;
7687 /* play the element sound at the position nearest to the player */
7688 if ((element == EL_MAGIC_WALL_FULL ||
7689 element == EL_MAGIC_WALL_ACTIVE ||
7690 element == EL_MAGIC_WALL_EMPTYING ||
7691 element == EL_BD_MAGIC_WALL_FULL ||
7692 element == EL_BD_MAGIC_WALL_ACTIVE ||
7693 element == EL_BD_MAGIC_WALL_EMPTYING) &&
7694 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
7702 #if USE_NEW_AMOEBA_CODE
7703 /* new experimental amoeba growth stuff */
7705 if (!(FrameCounter % 8))
7708 static unsigned long random = 1684108901;
7710 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
7713 x = (random >> 10) % lev_fieldx;
7714 y = (random >> 20) % lev_fieldy;
7716 x = RND(lev_fieldx);
7717 y = RND(lev_fieldy);
7719 element = Feld[x][y];
7721 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7722 if (!IS_PLAYER(x,y) &&
7723 (element == EL_EMPTY ||
7724 element == EL_SAND ||
7725 element == EL_QUICKSAND_EMPTY ||
7726 element == EL_ACID_SPLASH_LEFT ||
7727 element == EL_ACID_SPLASH_RIGHT))
7729 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
7730 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
7731 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
7732 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
7733 Feld[x][y] = EL_AMOEBA_DROP;
7736 random = random * 129 + 1;
7742 if (game.explosions_delayed)
7745 game.explosions_delayed = FALSE;
7747 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7749 element = Feld[x][y];
7751 if (ExplodeField[x][y])
7752 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
7753 else if (element == EL_EXPLOSION)
7754 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7756 ExplodeField[x][y] = EX_NO_EXPLOSION;
7759 game.explosions_delayed = TRUE;
7762 if (game.magic_wall_active)
7764 if (!(game.magic_wall_time_left % 4))
7766 int element = Feld[magic_wall_x][magic_wall_y];
7768 if (element == EL_BD_MAGIC_WALL_FULL ||
7769 element == EL_BD_MAGIC_WALL_ACTIVE ||
7770 element == EL_BD_MAGIC_WALL_EMPTYING)
7771 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
7773 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
7776 if (game.magic_wall_time_left > 0)
7778 game.magic_wall_time_left--;
7779 if (!game.magic_wall_time_left)
7781 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7783 element = Feld[x][y];
7785 if (element == EL_MAGIC_WALL_ACTIVE ||
7786 element == EL_MAGIC_WALL_FULL)
7788 Feld[x][y] = EL_MAGIC_WALL_DEAD;
7789 DrawLevelField(x, y);
7791 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
7792 element == EL_BD_MAGIC_WALL_FULL)
7794 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7795 DrawLevelField(x, y);
7799 game.magic_wall_active = FALSE;
7804 if (game.light_time_left > 0)
7806 game.light_time_left--;
7808 if (game.light_time_left == 0)
7809 RedrawAllLightSwitchesAndInvisibleElements();
7812 if (game.timegate_time_left > 0)
7814 game.timegate_time_left--;
7816 if (game.timegate_time_left == 0)
7817 CloseAllOpenTimegates();
7820 for (i = 0; i < MAX_PLAYERS; i++)
7822 struct PlayerInfo *player = &stored_player[i];
7824 if (SHIELD_ON(player))
7826 if (player->shield_deadly_time_left)
7827 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
7828 else if (player->shield_normal_time_left)
7829 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
7833 if (TimeFrames >= FRAMES_PER_SECOND)
7838 for (i = 0; i < MAX_PLAYERS; i++)
7840 struct PlayerInfo *player = &stored_player[i];
7842 if (SHIELD_ON(player))
7844 player->shield_normal_time_left--;
7846 if (player->shield_deadly_time_left > 0)
7847 player->shield_deadly_time_left--;
7851 if (tape.recording || tape.playing)
7852 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
7858 if (TimeLeft <= 10 && setup.time_limit)
7859 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
7861 DrawGameValue_Time(TimeLeft);
7863 if (!TimeLeft && setup.time_limit)
7864 for (i = 0; i < MAX_PLAYERS; i++)
7865 KillHero(&stored_player[i]);
7867 else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
7868 DrawGameValue_Time(TimePlayed);
7872 PlayAllPlayersSound();
7874 if (options.debug) /* calculate frames per second */
7876 static unsigned long fps_counter = 0;
7877 static int fps_frames = 0;
7878 unsigned long fps_delay_ms = Counter() - fps_counter;
7882 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
7884 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
7887 fps_counter = Counter();
7890 redraw_mask |= REDRAW_FPS;
7894 if (stored_player[0].jx != stored_player[0].last_jx ||
7895 stored_player[0].jy != stored_player[0].last_jy)
7896 printf("::: %d, %d, %d, %d, %d\n",
7897 stored_player[0].MovDir,
7898 stored_player[0].MovPos,
7899 stored_player[0].GfxPos,
7900 stored_player[0].Frame,
7901 stored_player[0].StepFrame);
7908 for (i = 0; i < MAX_PLAYERS; i++)
7911 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
7913 stored_player[i].Frame += move_frames;
7915 if (stored_player[i].MovPos != 0)
7916 stored_player[i].StepFrame += move_frames;
7918 if (stored_player[i].drop_delay > 0)
7919 stored_player[i].drop_delay--;
7924 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
7926 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
7928 local_player->show_envelope = 0;
7933 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
7935 int min_x = x, min_y = y, max_x = x, max_y = y;
7938 for (i = 0; i < MAX_PLAYERS; i++)
7940 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7942 if (!stored_player[i].active || &stored_player[i] == player)
7945 min_x = MIN(min_x, jx);
7946 min_y = MIN(min_y, jy);
7947 max_x = MAX(max_x, jx);
7948 max_y = MAX(max_y, jy);
7951 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
7954 static boolean AllPlayersInVisibleScreen()
7958 for (i = 0; i < MAX_PLAYERS; i++)
7960 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7962 if (!stored_player[i].active)
7965 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7972 void ScrollLevel(int dx, int dy)
7974 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
7977 BlitBitmap(drawto_field, drawto_field,
7978 FX + TILEX * (dx == -1) - softscroll_offset,
7979 FY + TILEY * (dy == -1) - softscroll_offset,
7980 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
7981 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
7982 FX + TILEX * (dx == 1) - softscroll_offset,
7983 FY + TILEY * (dy == 1) - softscroll_offset);
7987 x = (dx == 1 ? BX1 : BX2);
7988 for (y = BY1; y <= BY2; y++)
7989 DrawScreenField(x, y);
7994 y = (dy == 1 ? BY1 : BY2);
7995 for (x = BX1; x <= BX2; x++)
7996 DrawScreenField(x, y);
7999 redraw_mask |= REDRAW_FIELD;
8002 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
8004 int nextx = x + dx, nexty = y + dy;
8005 int element = Feld[x][y];
8008 element != EL_SP_PORT_LEFT &&
8009 element != EL_SP_GRAVITY_PORT_LEFT &&
8010 element != EL_SP_PORT_HORIZONTAL &&
8011 element != EL_SP_PORT_ANY) ||
8013 element != EL_SP_PORT_RIGHT &&
8014 element != EL_SP_GRAVITY_PORT_RIGHT &&
8015 element != EL_SP_PORT_HORIZONTAL &&
8016 element != EL_SP_PORT_ANY) ||
8018 element != EL_SP_PORT_UP &&
8019 element != EL_SP_GRAVITY_PORT_UP &&
8020 element != EL_SP_PORT_VERTICAL &&
8021 element != EL_SP_PORT_ANY) ||
8023 element != EL_SP_PORT_DOWN &&
8024 element != EL_SP_GRAVITY_PORT_DOWN &&
8025 element != EL_SP_PORT_VERTICAL &&
8026 element != EL_SP_PORT_ANY) ||
8027 !IN_LEV_FIELD(nextx, nexty) ||
8028 !IS_FREE(nextx, nexty))
8034 static void CheckGravityMovement(struct PlayerInfo *player)
8036 if (game.gravity && !player->programmed_action)
8038 int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
8039 int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
8041 (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
8042 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
8043 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
8044 int jx = player->jx, jy = player->jy;
8045 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8046 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8047 int new_jx = jx + dx, new_jy = jy + dy;
8048 boolean field_under_player_is_free =
8049 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
8050 boolean player_is_moving_to_valid_field =
8051 (IN_LEV_FIELD(new_jx, new_jy) &&
8052 (Feld[new_jx][new_jy] == EL_SP_BASE ||
8053 Feld[new_jx][new_jy] == EL_SAND ||
8054 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
8055 canEnterSupaplexPort(new_jx, new_jy, dx, dy))));
8056 /* !!! extend EL_SAND to anything diggable !!! */
8058 boolean player_is_standing_on_valid_field =
8059 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
8060 (IS_WALKABLE(Feld[jx][jy]) &&
8061 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
8063 if (field_under_player_is_free &&
8064 !player_is_standing_on_valid_field &&
8065 !player_is_moving_to_valid_field)
8066 player->programmed_action = MV_DOWN;
8072 -----------------------------------------------------------------------------
8073 dx, dy: direction (non-diagonal) to try to move the player to
8074 real_dx, real_dy: direction as read from input device (can be diagonal)
8077 boolean MovePlayerOneStep(struct PlayerInfo *player,
8078 int dx, int dy, int real_dx, int real_dy)
8081 static int trigger_sides[4][2] =
8083 /* enter side leave side */
8084 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
8085 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
8086 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
8087 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
8089 int move_direction = (dx == -1 ? MV_LEFT :
8090 dx == +1 ? MV_RIGHT :
8092 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8093 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
8094 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
8096 int jx = player->jx, jy = player->jy;
8097 int new_jx = jx + dx, new_jy = jy + dy;
8101 if (!player->active || (!dx && !dy))
8102 return MF_NO_ACTION;
8104 player->MovDir = (dx < 0 ? MV_LEFT :
8107 dy > 0 ? MV_DOWN : MV_NO_MOVING);
8109 if (!IN_LEV_FIELD(new_jx, new_jy))
8110 return MF_NO_ACTION;
8112 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
8113 return MF_NO_ACTION;
8116 element = MovingOrBlocked2Element(new_jx, new_jy);
8118 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
8121 if (DONT_RUN_INTO(element))
8123 if (element == EL_ACID && dx == 0 && dy == 1)
8125 SplashAcid(new_jx, new_jy);
8126 Feld[jx][jy] = EL_PLAYER_1;
8127 InitMovingField(jx, jy, MV_DOWN);
8128 Store[jx][jy] = EL_ACID;
8129 ContinueMoving(jx, jy);
8133 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
8138 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
8139 if (can_move != MF_MOVING)
8142 /* check if DigField() has caused relocation of the player */
8143 if (player->jx != jx || player->jy != jy)
8144 return MF_NO_ACTION;
8146 StorePlayer[jx][jy] = 0;
8147 player->last_jx = jx;
8148 player->last_jy = jy;
8149 player->jx = new_jx;
8150 player->jy = new_jy;
8151 StorePlayer[new_jx][new_jy] = player->element_nr;
8154 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
8156 player->step_counter++;
8158 player->drop_delay = 0;
8160 PlayerVisit[jx][jy] = FrameCounter;
8162 ScrollPlayer(player, SCROLL_INIT);
8165 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
8167 CheckTriggeredElementChangeSide(jx, jy, Feld[jx][jy], CE_OTHER_GETS_LEFT,
8169 CheckElementChangeSide(jx, jy, Feld[jx][jy], CE_LEFT_BY_PLAYER,leave_side);
8172 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
8174 CheckTriggeredElementChangeSide(new_jx, new_jy, Feld[new_jx][new_jy],
8175 CE_OTHER_GETS_ENTERED, enter_side);
8176 CheckElementChangeSide(new_jx, new_jy, Feld[new_jx][new_jy],
8177 CE_ENTERED_BY_PLAYER, enter_side);
8184 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
8186 int jx = player->jx, jy = player->jy;
8187 int old_jx = jx, old_jy = jy;
8188 int moved = MF_NO_ACTION;
8191 if (!player->active)
8196 if (player->MovPos == 0)
8198 player->is_moving = FALSE;
8199 player->is_digging = FALSE;
8200 player->is_collecting = FALSE;
8201 player->is_snapping = FALSE;
8202 player->is_pushing = FALSE;
8208 if (!player->active || (!dx && !dy))
8213 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
8217 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
8218 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
8222 /* remove the last programmed player action */
8223 player->programmed_action = 0;
8227 /* should only happen if pre-1.2 tape recordings are played */
8228 /* this is only for backward compatibility */
8230 int original_move_delay_value = player->move_delay_value;
8233 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
8237 /* scroll remaining steps with finest movement resolution */
8238 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
8240 while (player->MovPos)
8242 ScrollPlayer(player, SCROLL_GO_ON);
8243 ScrollScreen(NULL, SCROLL_GO_ON);
8249 player->move_delay_value = original_move_delay_value;
8252 if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
8254 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
8255 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
8259 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
8260 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
8266 if (moved & MF_MOVING && !ScreenMovPos &&
8267 (player == local_player || !options.network))
8269 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
8270 int offset = (setup.scroll_delay ? 3 : 0);
8272 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8274 /* actual player has left the screen -- scroll in that direction */
8275 if (jx != old_jx) /* player has moved horizontally */
8276 scroll_x += (jx - old_jx);
8277 else /* player has moved vertically */
8278 scroll_y += (jy - old_jy);
8282 if (jx != old_jx) /* player has moved horizontally */
8284 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
8285 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
8286 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
8288 /* don't scroll over playfield boundaries */
8289 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
8290 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
8292 /* don't scroll more than one field at a time */
8293 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
8295 /* don't scroll against the player's moving direction */
8296 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
8297 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
8298 scroll_x = old_scroll_x;
8300 else /* player has moved vertically */
8302 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
8303 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
8304 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
8306 /* don't scroll over playfield boundaries */
8307 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
8308 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
8310 /* don't scroll more than one field at a time */
8311 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
8313 /* don't scroll against the player's moving direction */
8314 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
8315 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
8316 scroll_y = old_scroll_y;
8320 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
8322 if (!options.network && !AllPlayersInVisibleScreen())
8324 scroll_x = old_scroll_x;
8325 scroll_y = old_scroll_y;
8329 ScrollScreen(player, SCROLL_INIT);
8330 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
8337 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
8339 if (!(moved & MF_MOVING) && !player->is_pushing)
8344 player->StepFrame = 0;
8346 if (moved & MF_MOVING)
8348 if (old_jx != jx && old_jy == jy)
8349 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
8350 else if (old_jx == jx && old_jy != jy)
8351 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
8353 DrawLevelField(jx, jy); /* for "crumbled sand" */
8355 player->last_move_dir = player->MovDir;
8356 player->is_moving = TRUE;
8358 player->is_snapping = FALSE;
8362 player->is_switching = FALSE;
8365 player->is_dropping = FALSE;
8370 static int trigger_sides[4][2] =
8372 /* enter side leave side */
8373 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
8374 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
8375 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
8376 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
8378 int move_direction = player->MovDir;
8379 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
8380 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
8383 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
8385 CheckTriggeredElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
8387 player->index_bit, leave_side);
8388 CheckElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
8390 player->index_bit, leave_side);
8393 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
8395 CheckTriggeredElementChangePlayer(jx, jy, Feld[jx][jy],
8396 CE_OTHER_GETS_ENTERED,
8397 player->index_bit, enter_side);
8398 CheckElementChangePlayer(jx, jy, Feld[jx][jy], CE_ENTERED_BY_PLAYER,
8399 player->index_bit, enter_side);
8410 CheckGravityMovement(player);
8413 player->last_move_dir = MV_NO_MOVING;
8415 player->is_moving = FALSE;
8418 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8420 TestIfHeroTouchesBadThing(jx, jy);
8421 TestIfPlayerTouchesCustomElement(jx, jy);
8424 if (!player->active)
8430 void ScrollPlayer(struct PlayerInfo *player, int mode)
8432 int jx = player->jx, jy = player->jy;
8433 int last_jx = player->last_jx, last_jy = player->last_jy;
8434 int move_stepsize = TILEX / player->move_delay_value;
8436 if (!player->active || !player->MovPos)
8439 if (mode == SCROLL_INIT)
8441 player->actual_frame_counter = FrameCounter;
8442 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8444 if (Feld[last_jx][last_jy] == EL_EMPTY)
8445 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
8453 else if (!FrameReached(&player->actual_frame_counter, 1))
8456 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
8457 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8459 if (!player->block_last_field &&
8460 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
8461 Feld[last_jx][last_jy] = EL_EMPTY;
8463 /* before DrawPlayer() to draw correct player graphic for this case */
8464 if (player->MovPos == 0)
8465 CheckGravityMovement(player);
8468 DrawPlayer(player); /* needed here only to cleanup last field */
8471 if (player->MovPos == 0) /* player reached destination field */
8474 if (player->move_delay_reset_counter > 0)
8476 player->move_delay_reset_counter--;
8478 if (player->move_delay_reset_counter == 0)
8480 /* continue with normal speed after quickly moving through gate */
8481 HALVE_PLAYER_SPEED(player);
8483 /* be able to make the next move without delay */
8484 player->move_delay = 0;
8488 if (IS_PASSABLE(Feld[last_jx][last_jy]))
8490 /* continue with normal speed after quickly moving through gate */
8491 HALVE_PLAYER_SPEED(player);
8493 /* be able to make the next move without delay */
8494 player->move_delay = 0;
8498 if (player->block_last_field &&
8499 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
8500 Feld[last_jx][last_jy] = EL_EMPTY;
8502 player->last_jx = jx;
8503 player->last_jy = jy;
8505 if (Feld[jx][jy] == EL_EXIT_OPEN ||
8506 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
8507 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
8509 DrawPlayer(player); /* needed here only to cleanup last field */
8512 if (local_player->friends_still_needed == 0 ||
8513 IS_SP_ELEMENT(Feld[jx][jy]))
8514 player->LevelSolved = player->GameOver = TRUE;
8517 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8519 TestIfHeroTouchesBadThing(jx, jy);
8520 TestIfPlayerTouchesCustomElement(jx, jy);
8522 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
8525 if (!player->active)
8529 if (tape.single_step && tape.recording && !tape.pausing &&
8530 !player->programmed_action)
8531 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8535 void ScrollScreen(struct PlayerInfo *player, int mode)
8537 static unsigned long screen_frame_counter = 0;
8539 if (mode == SCROLL_INIT)
8541 /* set scrolling step size according to actual player's moving speed */
8542 ScrollStepSize = TILEX / player->move_delay_value;
8544 screen_frame_counter = FrameCounter;
8545 ScreenMovDir = player->MovDir;
8546 ScreenMovPos = player->MovPos;
8547 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8550 else if (!FrameReached(&screen_frame_counter, 1))
8555 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
8556 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8557 redraw_mask |= REDRAW_FIELD;
8560 ScreenMovDir = MV_NO_MOVING;
8563 void TestIfPlayerTouchesCustomElement(int x, int y)
8565 static int xy[4][2] =
8572 static int trigger_sides[4][2] =
8574 /* center side border side */
8575 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8576 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8577 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8578 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8580 static int touch_dir[4] =
8587 int center_element = Feld[x][y]; /* should always be non-moving! */
8590 for (i = 0; i < NUM_DIRECTIONS; i++)
8592 int xx = x + xy[i][0];
8593 int yy = y + xy[i][1];
8594 int center_side = trigger_sides[i][0];
8595 int border_side = trigger_sides[i][1];
8598 if (!IN_LEV_FIELD(xx, yy))
8601 if (IS_PLAYER(x, y))
8603 struct PlayerInfo *player = PLAYERINFO(x, y);
8605 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8606 border_element = Feld[xx][yy]; /* may be moving! */
8607 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8608 border_element = Feld[xx][yy];
8609 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8610 border_element = MovingOrBlocked2Element(xx, yy);
8612 continue; /* center and border element do not touch */
8614 CheckTriggeredElementChangePlayer(xx, yy, border_element,
8615 CE_OTHER_GETS_TOUCHED,
8616 player->index_bit, border_side);
8617 CheckElementChangePlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
8618 player->index_bit, border_side);
8620 else if (IS_PLAYER(xx, yy))
8622 struct PlayerInfo *player = PLAYERINFO(xx, yy);
8624 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8626 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8627 continue; /* center and border element do not touch */
8630 CheckTriggeredElementChangePlayer(x, y, center_element,
8631 CE_OTHER_GETS_TOUCHED,
8632 player->index_bit, center_side);
8633 CheckElementChangePlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
8634 player->index_bit, center_side);
8641 void TestIfElementTouchesCustomElement(int x, int y)
8643 static int xy[4][2] =
8650 static int trigger_sides[4][2] =
8652 /* center side border side */
8653 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8654 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8655 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8656 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8658 static int touch_dir[4] =
8665 boolean change_center_element = FALSE;
8666 int center_element_change_page = 0;
8667 int center_element = Feld[x][y]; /* should always be non-moving! */
8670 for (i = 0; i < NUM_DIRECTIONS; i++)
8672 int xx = x + xy[i][0];
8673 int yy = y + xy[i][1];
8674 int center_side = trigger_sides[i][0];
8675 int border_side = trigger_sides[i][1];
8678 if (!IN_LEV_FIELD(xx, yy))
8681 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8682 border_element = Feld[xx][yy]; /* may be moving! */
8683 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8684 border_element = Feld[xx][yy];
8685 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8686 border_element = MovingOrBlocked2Element(xx, yy);
8688 continue; /* center and border element do not touch */
8690 /* check for change of center element (but change it only once) */
8691 if (IS_CUSTOM_ELEMENT(center_element) &&
8692 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
8693 !change_center_element)
8695 for (j = 0; j < element_info[center_element].num_change_pages; j++)
8697 struct ElementChangeInfo *change =
8698 &element_info[center_element].change_page[j];
8700 if (change->can_change &&
8701 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8702 change->trigger_side & border_side &&
8704 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
8706 change->trigger_element == border_element
8710 change_center_element = TRUE;
8711 center_element_change_page = j;
8718 /* check for change of border element */
8719 if (IS_CUSTOM_ELEMENT(border_element) &&
8720 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
8722 for (j = 0; j < element_info[border_element].num_change_pages; j++)
8724 struct ElementChangeInfo *change =
8725 &element_info[border_element].change_page[j];
8727 if (change->can_change &&
8728 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8729 change->trigger_side & center_side &&
8731 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
8733 change->trigger_element == center_element
8737 CheckElementChangePage(xx, yy, border_element, CE_OTHER_IS_TOUCHING,
8745 if (change_center_element)
8746 CheckElementChangePage(x, y, center_element, CE_OTHER_IS_TOUCHING,
8747 center_element_change_page);
8750 void TestIfElementHitsCustomElement(int x, int y, int direction)
8752 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8753 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8754 int hitx = x + dx, hity = y + dy;
8755 int hitting_element = Feld[x][y];
8757 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
8758 !IS_FREE(hitx, hity) &&
8759 (!IS_MOVING(hitx, hity) ||
8760 MovDir[hitx][hity] != direction ||
8761 ABS(MovPos[hitx][hity]) <= TILEY / 2));
8764 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
8768 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
8772 CheckElementChangeSide(x, y, hitting_element, CE_HITTING_SOMETHING,
8775 if (IN_LEV_FIELD(hitx, hity))
8777 int opposite_direction = MV_DIR_OPPOSITE(direction);
8778 int hitting_side = direction;
8779 int touched_side = opposite_direction;
8780 int touched_element = MovingOrBlocked2Element(hitx, hity);
8782 boolean object_hit = (!IS_MOVING(hitx, hity) ||
8783 MovDir[hitx][hity] != direction ||
8784 ABS(MovPos[hitx][hity]) <= TILEY / 2);
8793 CheckElementChangeSide(hitx, hity, touched_element, CE_HIT_BY_SOMETHING,
8794 opposite_direction);
8796 if (IS_CUSTOM_ELEMENT(hitting_element) &&
8797 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
8799 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
8801 struct ElementChangeInfo *change =
8802 &element_info[hitting_element].change_page[i];
8804 if (change->can_change &&
8805 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
8806 change->trigger_side & touched_side &&
8809 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
8811 change->trigger_element == touched_element
8815 CheckElementChangePage(x, y, hitting_element, CE_OTHER_IS_HITTING,
8822 if (IS_CUSTOM_ELEMENT(touched_element) &&
8823 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
8825 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
8827 struct ElementChangeInfo *change =
8828 &element_info[touched_element].change_page[i];
8830 if (change->can_change &&
8831 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
8832 change->trigger_side & hitting_side &&
8834 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
8836 change->trigger_element == hitting_element
8840 CheckElementChangePage(hitx, hity, touched_element,
8841 CE_OTHER_GETS_HIT, i);
8850 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
8852 int i, kill_x = -1, kill_y = -1;
8853 static int test_xy[4][2] =
8860 static int test_dir[4] =
8868 for (i = 0; i < NUM_DIRECTIONS; i++)
8870 int test_x, test_y, test_move_dir, test_element;
8872 test_x = good_x + test_xy[i][0];
8873 test_y = good_y + test_xy[i][1];
8874 if (!IN_LEV_FIELD(test_x, test_y))
8878 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8881 test_element = Feld[test_x][test_y];
8883 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
8886 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8887 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8889 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
8890 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
8898 if (kill_x != -1 || kill_y != -1)
8900 if (IS_PLAYER(good_x, good_y))
8902 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
8904 if (player->shield_deadly_time_left > 0)
8905 Bang(kill_x, kill_y);
8906 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
8910 Bang(good_x, good_y);
8914 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
8916 int i, kill_x = -1, kill_y = -1;
8917 int bad_element = Feld[bad_x][bad_y];
8918 static int test_xy[4][2] =
8925 static int touch_dir[4] =
8932 static int test_dir[4] =
8940 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
8943 for (i = 0; i < NUM_DIRECTIONS; i++)
8945 int test_x, test_y, test_move_dir, test_element;
8947 test_x = bad_x + test_xy[i][0];
8948 test_y = bad_y + test_xy[i][1];
8949 if (!IN_LEV_FIELD(test_x, test_y))
8953 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8955 test_element = Feld[test_x][test_y];
8957 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8958 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8960 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
8961 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
8963 /* good thing is player or penguin that does not move away */
8964 if (IS_PLAYER(test_x, test_y))
8966 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
8968 if (bad_element == EL_ROBOT && player->is_moving)
8969 continue; /* robot does not kill player if he is moving */
8971 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8973 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8974 continue; /* center and border element do not touch */
8981 else if (test_element == EL_PENGUIN)
8990 if (kill_x != -1 || kill_y != -1)
8992 if (IS_PLAYER(kill_x, kill_y))
8994 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
8996 if (player->shield_deadly_time_left > 0)
8998 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
9002 Bang(kill_x, kill_y);
9006 void TestIfHeroTouchesBadThing(int x, int y)
9008 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
9011 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
9013 TestIfGoodThingHitsBadThing(x, y, move_dir);
9016 void TestIfBadThingTouchesHero(int x, int y)
9018 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
9021 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
9023 TestIfBadThingHitsGoodThing(x, y, move_dir);
9026 void TestIfFriendTouchesBadThing(int x, int y)
9028 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
9031 void TestIfBadThingTouchesFriend(int x, int y)
9033 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
9036 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
9038 int i, kill_x = bad_x, kill_y = bad_y;
9039 static int xy[4][2] =
9047 for (i = 0; i < NUM_DIRECTIONS; i++)
9051 x = bad_x + xy[i][0];
9052 y = bad_y + xy[i][1];
9053 if (!IN_LEV_FIELD(x, y))
9056 element = Feld[x][y];
9057 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
9058 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
9066 if (kill_x != bad_x || kill_y != bad_y)
9070 void KillHero(struct PlayerInfo *player)
9072 int jx = player->jx, jy = player->jy;
9074 if (!player->active)
9077 /* remove accessible field at the player's position */
9078 Feld[jx][jy] = EL_EMPTY;
9080 /* deactivate shield (else Bang()/Explode() would not work right) */
9081 player->shield_normal_time_left = 0;
9082 player->shield_deadly_time_left = 0;
9088 static void KillHeroUnlessEnemyProtected(int x, int y)
9090 if (!PLAYER_ENEMY_PROTECTED(x, y))
9091 KillHero(PLAYERINFO(x, y));
9094 static void KillHeroUnlessExplosionProtected(int x, int y)
9096 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
9097 KillHero(PLAYERINFO(x, y));
9100 void BuryHero(struct PlayerInfo *player)
9102 int jx = player->jx, jy = player->jy;
9104 if (!player->active)
9108 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
9110 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
9112 PlayLevelSound(jx, jy, SND_GAME_LOSING);
9114 player->GameOver = TRUE;
9118 void RemoveHero(struct PlayerInfo *player)
9120 int jx = player->jx, jy = player->jy;
9121 int i, found = FALSE;
9123 player->present = FALSE;
9124 player->active = FALSE;
9126 if (!ExplodeField[jx][jy])
9127 StorePlayer[jx][jy] = 0;
9129 for (i = 0; i < MAX_PLAYERS; i++)
9130 if (stored_player[i].active)
9134 AllPlayersGone = TRUE;
9141 =============================================================================
9142 checkDiagonalPushing()
9143 -----------------------------------------------------------------------------
9144 check if diagonal input device direction results in pushing of object
9145 (by checking if the alternative direction is walkable, diggable, ...)
9146 =============================================================================
9149 static boolean checkDiagonalPushing(struct PlayerInfo *player,
9150 int x, int y, int real_dx, int real_dy)
9152 int jx, jy, dx, dy, xx, yy;
9154 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
9157 /* diagonal direction: check alternative direction */
9162 xx = jx + (dx == 0 ? real_dx : 0);
9163 yy = jy + (dy == 0 ? real_dy : 0);
9165 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
9169 =============================================================================
9171 -----------------------------------------------------------------------------
9172 x, y: field next to player (non-diagonal) to try to dig to
9173 real_dx, real_dy: direction as read from input device (can be diagonal)
9174 =============================================================================
9177 int DigField(struct PlayerInfo *player,
9178 int oldx, int oldy, int x, int y,
9179 int real_dx, int real_dy, int mode)
9181 static int trigger_sides[4] =
9183 CH_SIDE_RIGHT, /* moving left */
9184 CH_SIDE_LEFT, /* moving right */
9185 CH_SIDE_BOTTOM, /* moving up */
9186 CH_SIDE_TOP, /* moving down */
9189 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
9191 int jx = oldx, jy = oldy;
9192 int dx = x - jx, dy = y - jy;
9193 int nextx = x + dx, nexty = y + dy;
9194 int move_direction = (dx == -1 ? MV_LEFT :
9195 dx == +1 ? MV_RIGHT :
9197 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9198 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
9199 int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
9200 int old_element = Feld[jx][jy];
9203 if (player->MovPos == 0)
9205 player->is_digging = FALSE;
9206 player->is_collecting = FALSE;
9209 if (player->MovPos == 0) /* last pushing move finished */
9210 player->is_pushing = FALSE;
9212 if (mode == DF_NO_PUSH) /* player just stopped pushing */
9214 player->is_switching = FALSE;
9215 player->push_delay = 0;
9217 return MF_NO_ACTION;
9220 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
9221 return MF_NO_ACTION;
9226 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
9228 if (IS_TUBE(Feld[jx][jy]) ||
9229 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
9233 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
9234 int tube_leave_directions[][2] =
9236 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
9237 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
9238 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
9239 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
9240 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
9241 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
9242 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
9243 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
9244 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
9245 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
9246 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
9247 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
9250 while (tube_leave_directions[i][0] != tube_element)
9253 if (tube_leave_directions[i][0] == -1) /* should not happen */
9257 if (!(tube_leave_directions[i][1] & move_direction))
9258 return MF_NO_ACTION; /* tube has no opening in this direction */
9263 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
9264 old_element = Back[jx][jy];
9268 if (IS_WALKABLE(old_element) &&
9269 !(element_info[old_element].access_direction & move_direction))
9270 return MF_NO_ACTION; /* field has no opening in this direction */
9272 element = Feld[x][y];
9274 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
9275 game.engine_version >= VERSION_IDENT(2,2,0,0))
9276 return MF_NO_ACTION;
9280 case EL_SP_PORT_LEFT:
9281 case EL_SP_PORT_RIGHT:
9283 case EL_SP_PORT_DOWN:
9284 case EL_SP_PORT_HORIZONTAL:
9285 case EL_SP_PORT_VERTICAL:
9286 case EL_SP_PORT_ANY:
9287 case EL_SP_GRAVITY_PORT_LEFT:
9288 case EL_SP_GRAVITY_PORT_RIGHT:
9289 case EL_SP_GRAVITY_PORT_UP:
9290 case EL_SP_GRAVITY_PORT_DOWN:
9292 if (!canEnterSupaplexPort(x, y, dx, dy))
9293 return MF_NO_ACTION;
9296 element != EL_SP_PORT_LEFT &&
9297 element != EL_SP_GRAVITY_PORT_LEFT &&
9298 element != EL_SP_PORT_HORIZONTAL &&
9299 element != EL_SP_PORT_ANY) ||
9301 element != EL_SP_PORT_RIGHT &&
9302 element != EL_SP_GRAVITY_PORT_RIGHT &&
9303 element != EL_SP_PORT_HORIZONTAL &&
9304 element != EL_SP_PORT_ANY) ||
9306 element != EL_SP_PORT_UP &&
9307 element != EL_SP_GRAVITY_PORT_UP &&
9308 element != EL_SP_PORT_VERTICAL &&
9309 element != EL_SP_PORT_ANY) ||
9311 element != EL_SP_PORT_DOWN &&
9312 element != EL_SP_GRAVITY_PORT_DOWN &&
9313 element != EL_SP_PORT_VERTICAL &&
9314 element != EL_SP_PORT_ANY) ||
9315 !IN_LEV_FIELD(nextx, nexty) ||
9316 !IS_FREE(nextx, nexty))
9317 return MF_NO_ACTION;
9320 if (element == EL_SP_GRAVITY_PORT_LEFT ||
9321 element == EL_SP_GRAVITY_PORT_RIGHT ||
9322 element == EL_SP_GRAVITY_PORT_UP ||
9323 element == EL_SP_GRAVITY_PORT_DOWN)
9324 game.gravity = !game.gravity;
9326 /* automatically move to the next field with double speed */
9327 player->programmed_action = move_direction;
9329 if (player->move_delay_reset_counter == 0)
9331 player->move_delay_reset_counter = 2; /* two double speed steps */
9333 DOUBLE_PLAYER_SPEED(player);
9336 player->move_delay_reset_counter = 2;
9338 DOUBLE_PLAYER_SPEED(player);
9341 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
9346 case EL_TUBE_VERTICAL:
9347 case EL_TUBE_HORIZONTAL:
9348 case EL_TUBE_VERTICAL_LEFT:
9349 case EL_TUBE_VERTICAL_RIGHT:
9350 case EL_TUBE_HORIZONTAL_UP:
9351 case EL_TUBE_HORIZONTAL_DOWN:
9352 case EL_TUBE_LEFT_UP:
9353 case EL_TUBE_LEFT_DOWN:
9354 case EL_TUBE_RIGHT_UP:
9355 case EL_TUBE_RIGHT_DOWN:
9358 int tube_enter_directions[][2] =
9360 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
9361 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
9362 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
9363 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
9364 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
9365 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
9366 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
9367 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
9368 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
9369 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
9370 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
9371 { -1, MV_NO_MOVING }
9374 while (tube_enter_directions[i][0] != element)
9377 if (tube_enter_directions[i][0] == -1) /* should not happen */
9381 if (!(tube_enter_directions[i][1] & move_direction))
9382 return MF_NO_ACTION; /* tube has no opening in this direction */
9384 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
9391 if (IS_WALKABLE(element))
9393 int sound_action = ACTION_WALKING;
9395 if (!(element_info[element].access_direction & opposite_direction))
9396 return MF_NO_ACTION; /* field not accessible from this direction */
9398 if (element >= EL_GATE_1 && element <= EL_GATE_4)
9400 if (!player->key[element - EL_GATE_1])
9401 return MF_NO_ACTION;
9403 else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
9405 if (!player->key[element - EL_GATE_1_GRAY])
9406 return MF_NO_ACTION;
9408 else if (element == EL_EXIT_OPEN ||
9409 element == EL_SP_EXIT_OPEN ||
9410 element == EL_SP_EXIT_OPENING)
9412 sound_action = ACTION_PASSING; /* player is passing exit */
9414 else if (element == EL_EMPTY)
9416 sound_action = ACTION_MOVING; /* nothing to walk on */
9419 /* play sound from background or player, whatever is available */
9420 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
9421 PlayLevelSoundElementAction(x, y, element, sound_action);
9423 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
9427 else if (IS_PASSABLE(element))
9429 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
9430 return MF_NO_ACTION;
9432 if (IS_CUSTOM_ELEMENT(element) &&
9433 !(element_info[element].access_direction & opposite_direction))
9434 return MF_NO_ACTION; /* field not accessible from this direction */
9437 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
9438 return MF_NO_ACTION;
9441 if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
9443 if (!player->key[element - EL_EM_GATE_1])
9444 return MF_NO_ACTION;
9446 else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
9448 if (!player->key[element - EL_EM_GATE_1_GRAY])
9449 return MF_NO_ACTION;
9452 /* automatically move to the next field with double speed */
9453 player->programmed_action = move_direction;
9455 if (player->move_delay_reset_counter == 0)
9457 player->move_delay_reset_counter = 2; /* two double speed steps */
9459 DOUBLE_PLAYER_SPEED(player);
9462 player->move_delay_reset_counter = 2;
9464 DOUBLE_PLAYER_SPEED(player);
9467 PlayLevelSoundAction(x, y, ACTION_PASSING);
9471 else if (IS_DIGGABLE(element))
9475 if (mode != DF_SNAP)
9478 GfxElement[x][y] = GFX_ELEMENT(element);
9481 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
9483 player->is_digging = TRUE;
9486 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
9488 CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_DIGGED,
9489 player->index_bit, CH_SIDE_ANY);
9492 if (mode == DF_SNAP)
9493 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9498 else if (IS_COLLECTIBLE(element))
9502 if (mode != DF_SNAP)
9504 GfxElement[x][y] = element;
9505 player->is_collecting = TRUE;
9508 if (element == EL_SPEED_PILL)
9509 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
9510 else if (element == EL_EXTRA_TIME && level.time > 0)
9513 DrawGameValue_Time(TimeLeft);
9515 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
9517 player->shield_normal_time_left += 10;
9518 if (element == EL_SHIELD_DEADLY)
9519 player->shield_deadly_time_left += 10;
9521 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
9523 if (player->inventory_size < MAX_INVENTORY_SIZE)
9524 player->inventory_element[player->inventory_size++] = element;
9526 DrawGameValue_Dynamite(local_player->inventory_size);
9528 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
9530 player->dynabomb_count++;
9531 player->dynabombs_left++;
9533 else if (element == EL_DYNABOMB_INCREASE_SIZE)
9535 player->dynabomb_size++;
9537 else if (element == EL_DYNABOMB_INCREASE_POWER)
9539 player->dynabomb_xl = TRUE;
9541 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
9542 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
9544 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
9545 element - EL_KEY_1 : element - EL_EM_KEY_1);
9547 player->key[key_nr] = TRUE;
9549 DrawGameValue_Keys(player);
9551 redraw_mask |= REDRAW_DOOR_1;
9553 else if (IS_ENVELOPE(element))
9556 player->show_envelope = element;
9558 ShowEnvelope(element - EL_ENVELOPE_1);
9561 else if (IS_DROPPABLE(element)) /* can be collected and dropped */
9565 for (i = 0; i < element_info[element].collect_count; i++)
9566 if (player->inventory_size < MAX_INVENTORY_SIZE)
9567 player->inventory_element[player->inventory_size++] = element;
9569 DrawGameValue_Dynamite(local_player->inventory_size);
9571 else if (element_info[element].collect_count > 0)
9573 local_player->gems_still_needed -=
9574 element_info[element].collect_count;
9575 if (local_player->gems_still_needed < 0)
9576 local_player->gems_still_needed = 0;
9578 DrawGameValue_Emeralds(local_player->gems_still_needed);
9581 RaiseScoreElement(element);
9582 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
9584 CheckTriggeredElementChangePlayer(x, y, element,
9585 CE_OTHER_GETS_COLLECTED,
9586 player->index_bit, CH_SIDE_ANY);
9589 if (mode == DF_SNAP)
9590 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9595 else if (IS_PUSHABLE(element))
9597 if (mode == DF_SNAP && element != EL_BD_ROCK)
9598 return MF_NO_ACTION;
9600 if (CAN_FALL(element) && dy)
9601 return MF_NO_ACTION;
9603 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
9604 !(element == EL_SPRING && level.use_spring_bug))
9605 return MF_NO_ACTION;
9608 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
9609 ((move_direction & MV_VERTICAL &&
9610 ((element_info[element].move_pattern & MV_LEFT &&
9611 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
9612 (element_info[element].move_pattern & MV_RIGHT &&
9613 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
9614 (move_direction & MV_HORIZONTAL &&
9615 ((element_info[element].move_pattern & MV_UP &&
9616 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
9617 (element_info[element].move_pattern & MV_DOWN &&
9618 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
9619 return MF_NO_ACTION;
9623 /* do not push elements already moving away faster than player */
9624 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
9625 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
9626 return MF_NO_ACTION;
9628 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
9629 return MF_NO_ACTION;
9633 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9635 if (player->push_delay_value == -1)
9636 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9638 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
9640 if (!player->is_pushing)
9641 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9645 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
9646 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
9647 !player_is_pushing))
9648 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9651 if (!player->is_pushing &&
9652 game.engine_version >= VERSION_IDENT(2,2,0,7))
9653 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9657 printf("::: push delay: %ld [%d, %d] [%d]\n",
9658 player->push_delay_value, FrameCounter, game.engine_version,
9659 player->is_pushing);
9662 player->is_pushing = TRUE;
9664 if (!(IN_LEV_FIELD(nextx, nexty) &&
9665 (IS_FREE(nextx, nexty) ||
9666 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
9667 IS_SB_ELEMENT(element)))))
9668 return MF_NO_ACTION;
9670 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
9671 return MF_NO_ACTION;
9673 if (player->push_delay == 0) /* new pushing; restart delay */
9674 player->push_delay = FrameCounter;
9676 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
9677 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
9678 element != EL_SPRING && element != EL_BALLOON)
9680 /* make sure that there is no move delay before next try to push */
9681 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9682 player->move_delay = INITIAL_MOVE_DELAY_OFF;
9684 return MF_NO_ACTION;
9688 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
9691 if (IS_SB_ELEMENT(element))
9693 if (element == EL_SOKOBAN_FIELD_FULL)
9695 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
9696 local_player->sokobanfields_still_needed++;
9699 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
9701 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
9702 local_player->sokobanfields_still_needed--;
9705 Feld[x][y] = EL_SOKOBAN_OBJECT;
9707 if (Back[x][y] == Back[nextx][nexty])
9708 PlayLevelSoundAction(x, y, ACTION_PUSHING);
9709 else if (Back[x][y] != 0)
9710 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
9713 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
9716 if (local_player->sokobanfields_still_needed == 0 &&
9717 game.emulation == EMU_SOKOBAN)
9719 player->LevelSolved = player->GameOver = TRUE;
9720 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
9724 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
9726 InitMovingField(x, y, move_direction);
9727 GfxAction[x][y] = ACTION_PUSHING;
9729 if (mode == DF_SNAP)
9730 ContinueMoving(x, y);
9732 MovPos[x][y] = (dx != 0 ? dx : dy);
9734 Pushed[x][y] = TRUE;
9735 Pushed[nextx][nexty] = TRUE;
9737 if (game.engine_version < VERSION_IDENT(2,2,0,7))
9738 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9740 player->push_delay_value = -1; /* get new value later */
9742 CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_PUSHED,
9743 player->index_bit, dig_side);
9744 CheckElementChangePlayer(x, y, element, CE_PUSHED_BY_PLAYER,
9745 player->index_bit, dig_side);
9749 else if (IS_SWITCHABLE(element))
9751 if (PLAYER_SWITCHING(player, x, y))
9754 player->is_switching = TRUE;
9755 player->switch_x = x;
9756 player->switch_y = y;
9758 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
9760 if (element == EL_ROBOT_WHEEL)
9762 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
9766 DrawLevelField(x, y);
9768 else if (element == EL_SP_TERMINAL)
9772 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
9774 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
9776 else if (Feld[xx][yy] == EL_SP_TERMINAL)
9777 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
9780 else if (IS_BELT_SWITCH(element))
9782 ToggleBeltSwitch(x, y);
9784 else if (element == EL_SWITCHGATE_SWITCH_UP ||
9785 element == EL_SWITCHGATE_SWITCH_DOWN)
9787 ToggleSwitchgateSwitch(x, y);
9789 else if (element == EL_LIGHT_SWITCH ||
9790 element == EL_LIGHT_SWITCH_ACTIVE)
9792 ToggleLightSwitch(x, y);
9795 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
9796 SND_LIGHT_SWITCH_ACTIVATING :
9797 SND_LIGHT_SWITCH_DEACTIVATING);
9800 else if (element == EL_TIMEGATE_SWITCH)
9802 ActivateTimegateSwitch(x, y);
9804 else if (element == EL_BALLOON_SWITCH_LEFT ||
9805 element == EL_BALLOON_SWITCH_RIGHT ||
9806 element == EL_BALLOON_SWITCH_UP ||
9807 element == EL_BALLOON_SWITCH_DOWN ||
9808 element == EL_BALLOON_SWITCH_ANY)
9810 if (element == EL_BALLOON_SWITCH_ANY)
9811 game.balloon_dir = move_direction;
9813 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
9814 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
9815 element == EL_BALLOON_SWITCH_UP ? MV_UP :
9816 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
9819 else if (element == EL_LAMP)
9821 Feld[x][y] = EL_LAMP_ACTIVE;
9822 local_player->lights_still_needed--;
9824 DrawLevelField(x, y);
9826 else if (element == EL_TIME_ORB_FULL)
9828 Feld[x][y] = EL_TIME_ORB_EMPTY;
9830 DrawGameValue_Time(TimeLeft);
9832 DrawLevelField(x, y);
9835 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
9843 if (!PLAYER_SWITCHING(player, x, y))
9845 player->is_switching = TRUE;
9846 player->switch_x = x;
9847 player->switch_y = y;
9849 CheckTriggeredElementChangePlayer(x, y, element,
9850 CE_OTHER_IS_SWITCHING,
9851 player->index_bit, dig_side);
9852 CheckElementChangePlayer(x, y, element, CE_SWITCHED,
9853 player->index_bit, dig_side);
9856 CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_PRESSED,
9857 player->index_bit, dig_side);
9858 CheckElementChangePlayer(x, y, element, CE_PRESSED_BY_PLAYER,
9859 player->index_bit, dig_side);
9862 return MF_NO_ACTION;
9865 player->push_delay = 0;
9867 if (Feld[x][y] != element) /* really digged/collected something */
9868 player->is_collecting = !player->is_digging;
9873 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
9875 int jx = player->jx, jy = player->jy;
9876 int x = jx + dx, y = jy + dy;
9877 int snap_direction = (dx == -1 ? MV_LEFT :
9878 dx == +1 ? MV_RIGHT :
9880 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9882 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
9885 if (!player->active || !IN_LEV_FIELD(x, y))
9893 if (player->MovPos == 0)
9894 player->is_pushing = FALSE;
9896 player->is_snapping = FALSE;
9898 if (player->MovPos == 0)
9900 player->is_moving = FALSE;
9901 player->is_digging = FALSE;
9902 player->is_collecting = FALSE;
9908 if (player->is_snapping)
9911 player->MovDir = snap_direction;
9914 if (player->MovPos == 0)
9917 player->is_moving = FALSE;
9918 player->is_digging = FALSE;
9919 player->is_collecting = FALSE;
9922 player->is_dropping = FALSE;
9924 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
9927 player->is_snapping = TRUE;
9930 if (player->MovPos == 0)
9933 player->is_moving = FALSE;
9934 player->is_digging = FALSE;
9935 player->is_collecting = FALSE;
9938 DrawLevelField(x, y);
9944 boolean DropElement(struct PlayerInfo *player)
9946 int jx = player->jx, jy = player->jy;
9947 int old_element = Feld[jx][jy];
9950 /* check if player is active, not moving and ready to drop */
9951 if (!player->active || player->MovPos || player->drop_delay > 0)
9954 /* check if player has anything that can be dropped */
9955 if (player->inventory_size == 0 && player->dynabombs_left == 0)
9958 /* check if anything can be dropped at the current position */
9959 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
9962 /* collected custom elements can only be dropped on empty fields */
9963 if (player->inventory_size > 0 &&
9964 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
9965 && old_element != EL_EMPTY)
9968 if (old_element != EL_EMPTY)
9969 Back[jx][jy] = old_element; /* store old element on this field */
9971 ResetGfxAnimation(jx, jy);
9972 ResetRandomAnimationValue(jx, jy);
9974 if (player->inventory_size > 0)
9976 player->inventory_size--;
9977 new_element = player->inventory_element[player->inventory_size];
9979 if (new_element == EL_DYNAMITE)
9980 new_element = EL_DYNAMITE_ACTIVE;
9981 else if (new_element == EL_SP_DISK_RED)
9982 new_element = EL_SP_DISK_RED_ACTIVE;
9984 Feld[jx][jy] = new_element;
9986 DrawGameValue_Dynamite(local_player->inventory_size);
9988 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9989 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9991 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9994 /* needed if previous element just changed to "empty" in the last frame */
9995 Changed[jx][jy] = 0; /* allow another change */
9998 CheckTriggeredElementChangePlayer(jx, jy, new_element,
9999 CE_OTHER_GETS_DROPPED,
10000 player->index_bit, CH_SIDE_ANY);
10001 CheckElementChangePlayer(jx, jy, new_element, CE_DROPPED_BY_PLAYER,
10002 player->index_bit, CH_SIDE_ANY);
10004 TestIfElementTouchesCustomElement(jx, jy);
10006 else /* player is dropping a dyna bomb */
10008 player->dynabombs_left--;
10009 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
10011 Feld[jx][jy] = new_element;
10013 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
10014 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
10016 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
10023 if (Feld[jx][jy] == new_element) /* uninitialized unless CE change */
10026 InitField_WithBug1(jx, jy, FALSE);
10028 InitField(jx, jy, FALSE);
10029 if (CAN_MOVE(Feld[jx][jy]))
10030 InitMovDir(jx, jy);
10034 new_element = Feld[jx][jy];
10036 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
10037 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
10039 int move_stepsize = element_info[new_element].move_stepsize;
10040 int direction, dx, dy, nextx, nexty;
10042 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
10043 MovDir[jx][jy] = player->MovDir;
10045 direction = MovDir[jx][jy];
10046 dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10047 dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10051 if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
10054 WasJustMoving[jx][jy] = 3;
10056 InitMovingField(jx, jy, direction);
10057 ContinueMoving(jx, jy);
10062 Changed[jx][jy] = 0; /* allow another change */
10065 TestIfElementHitsCustomElement(jx, jy, direction);
10067 CheckElementChangeSide(jx, jy, new_element, CE_HITTING_SOMETHING,
10072 player->drop_delay = 2 * TILEX / move_stepsize + 1;
10076 player->drop_delay = 8 + 8 + 8;
10081 player->is_dropping = TRUE;
10087 /* ------------------------------------------------------------------------- */
10088 /* game sound playing functions */
10089 /* ------------------------------------------------------------------------- */
10091 static int *loop_sound_frame = NULL;
10092 static int *loop_sound_volume = NULL;
10094 void InitPlayLevelSound()
10096 int num_sounds = getSoundListSize();
10098 checked_free(loop_sound_frame);
10099 checked_free(loop_sound_volume);
10101 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
10102 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
10105 static void PlayLevelSound(int x, int y, int nr)
10107 int sx = SCREENX(x), sy = SCREENY(y);
10108 int volume, stereo_position;
10109 int max_distance = 8;
10110 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
10112 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
10113 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
10116 if (!IN_LEV_FIELD(x, y) ||
10117 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
10118 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
10121 volume = SOUND_MAX_VOLUME;
10123 if (!IN_SCR_FIELD(sx, sy))
10125 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
10126 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
10128 volume -= volume * (dx > dy ? dx : dy) / max_distance;
10131 stereo_position = (SOUND_MAX_LEFT +
10132 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
10133 (SCR_FIELDX + 2 * max_distance));
10135 if (IS_LOOP_SOUND(nr))
10137 /* This assures that quieter loop sounds do not overwrite louder ones,
10138 while restarting sound volume comparison with each new game frame. */
10140 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
10143 loop_sound_volume[nr] = volume;
10144 loop_sound_frame[nr] = FrameCounter;
10147 PlaySoundExt(nr, volume, stereo_position, type);
10150 static void PlayLevelSoundNearest(int x, int y, int sound_action)
10152 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
10153 x > LEVELX(BX2) ? LEVELX(BX2) : x,
10154 y < LEVELY(BY1) ? LEVELY(BY1) :
10155 y > LEVELY(BY2) ? LEVELY(BY2) : y,
10159 static void PlayLevelSoundAction(int x, int y, int action)
10161 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
10164 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
10166 int sound_effect = element_info[element].sound[action];
10168 if (sound_effect != SND_UNDEFINED)
10169 PlayLevelSound(x, y, sound_effect);
10172 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
10175 int sound_effect = element_info[element].sound[action];
10177 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10178 PlayLevelSound(x, y, sound_effect);
10181 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
10183 int sound_effect = element_info[Feld[x][y]].sound[action];
10185 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10186 PlayLevelSound(x, y, sound_effect);
10189 static void StopLevelSoundActionIfLoop(int x, int y, int action)
10191 int sound_effect = element_info[Feld[x][y]].sound[action];
10193 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10194 StopSound(sound_effect);
10197 static void PlayLevelMusic()
10199 if (levelset.music[level_nr] != MUS_UNDEFINED)
10200 PlayMusic(levelset.music[level_nr]); /* from config file */
10202 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
10205 void RaiseScore(int value)
10207 local_player->score += value;
10209 DrawGameValue_Score(local_player->score);
10212 void RaiseScoreElement(int element)
10217 case EL_BD_DIAMOND:
10218 case EL_EMERALD_YELLOW:
10219 case EL_EMERALD_RED:
10220 case EL_EMERALD_PURPLE:
10221 case EL_SP_INFOTRON:
10222 RaiseScore(level.score[SC_EMERALD]);
10225 RaiseScore(level.score[SC_DIAMOND]);
10228 RaiseScore(level.score[SC_CRYSTAL]);
10231 RaiseScore(level.score[SC_PEARL]);
10234 case EL_BD_BUTTERFLY:
10235 case EL_SP_ELECTRON:
10236 RaiseScore(level.score[SC_BUG]);
10239 case EL_BD_FIREFLY:
10240 case EL_SP_SNIKSNAK:
10241 RaiseScore(level.score[SC_SPACESHIP]);
10244 case EL_DARK_YAMYAM:
10245 RaiseScore(level.score[SC_YAMYAM]);
10248 RaiseScore(level.score[SC_ROBOT]);
10251 RaiseScore(level.score[SC_PACMAN]);
10254 RaiseScore(level.score[SC_NUT]);
10257 case EL_SP_DISK_RED:
10258 case EL_DYNABOMB_INCREASE_NUMBER:
10259 case EL_DYNABOMB_INCREASE_SIZE:
10260 case EL_DYNABOMB_INCREASE_POWER:
10261 RaiseScore(level.score[SC_DYNAMITE]);
10263 case EL_SHIELD_NORMAL:
10264 case EL_SHIELD_DEADLY:
10265 RaiseScore(level.score[SC_SHIELD]);
10267 case EL_EXTRA_TIME:
10268 RaiseScore(level.score[SC_TIME_BONUS]);
10274 RaiseScore(level.score[SC_KEY]);
10277 RaiseScore(element_info[element].collect_score);
10282 void RequestQuitGame(boolean ask_if_really_quit)
10284 if (AllPlayersGone ||
10285 !ask_if_really_quit ||
10286 level_editor_test_game ||
10287 Request("Do you really want to quit the game ?",
10288 REQ_ASK | REQ_STAY_CLOSED))
10290 #if defined(PLATFORM_UNIX)
10291 if (options.network)
10292 SendToServer_StopPlaying();
10296 game_status = GAME_MODE_MAIN;
10302 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
10307 /* ---------- new game button stuff ---------------------------------------- */
10309 /* graphic position values for game buttons */
10310 #define GAME_BUTTON_XSIZE 30
10311 #define GAME_BUTTON_YSIZE 30
10312 #define GAME_BUTTON_XPOS 5
10313 #define GAME_BUTTON_YPOS 215
10314 #define SOUND_BUTTON_XPOS 5
10315 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
10317 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10318 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10319 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10320 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10321 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10322 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10329 } gamebutton_info[NUM_GAME_BUTTONS] =
10332 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
10337 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
10338 GAME_CTRL_ID_PAUSE,
10342 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
10347 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
10348 SOUND_CTRL_ID_MUSIC,
10349 "background music on/off"
10352 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
10353 SOUND_CTRL_ID_LOOPS,
10354 "sound loops on/off"
10357 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
10358 SOUND_CTRL_ID_SIMPLE,
10359 "normal sounds on/off"
10363 void CreateGameButtons()
10367 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10369 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
10370 struct GadgetInfo *gi;
10373 unsigned long event_mask;
10374 int gd_xoffset, gd_yoffset;
10375 int gd_x1, gd_x2, gd_y1, gd_y2;
10378 gd_xoffset = gamebutton_info[i].x;
10379 gd_yoffset = gamebutton_info[i].y;
10380 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
10381 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
10383 if (id == GAME_CTRL_ID_STOP ||
10384 id == GAME_CTRL_ID_PAUSE ||
10385 id == GAME_CTRL_ID_PLAY)
10387 button_type = GD_TYPE_NORMAL_BUTTON;
10389 event_mask = GD_EVENT_RELEASED;
10390 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10391 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10395 button_type = GD_TYPE_CHECK_BUTTON;
10397 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
10398 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
10399 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
10400 event_mask = GD_EVENT_PRESSED;
10401 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
10402 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10405 gi = CreateGadget(GDI_CUSTOM_ID, id,
10406 GDI_INFO_TEXT, gamebutton_info[i].infotext,
10407 GDI_X, DX + gd_xoffset,
10408 GDI_Y, DY + gd_yoffset,
10409 GDI_WIDTH, GAME_BUTTON_XSIZE,
10410 GDI_HEIGHT, GAME_BUTTON_YSIZE,
10411 GDI_TYPE, button_type,
10412 GDI_STATE, GD_BUTTON_UNPRESSED,
10413 GDI_CHECKED, checked,
10414 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
10415 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
10416 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
10417 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
10418 GDI_EVENT_MASK, event_mask,
10419 GDI_CALLBACK_ACTION, HandleGameButtons,
10423 Error(ERR_EXIT, "cannot create gadget");
10425 game_gadget[id] = gi;
10429 void FreeGameButtons()
10433 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10434 FreeGadget(game_gadget[i]);
10437 static void MapGameButtons()
10441 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10442 MapGadget(game_gadget[i]);
10445 void UnmapGameButtons()
10449 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10450 UnmapGadget(game_gadget[i]);
10453 static void HandleGameButtons(struct GadgetInfo *gi)
10455 int id = gi->custom_id;
10457 if (game_status != GAME_MODE_PLAYING)
10462 case GAME_CTRL_ID_STOP:
10463 RequestQuitGame(TRUE);
10466 case GAME_CTRL_ID_PAUSE:
10467 if (options.network)
10469 #if defined(PLATFORM_UNIX)
10471 SendToServer_ContinuePlaying();
10473 SendToServer_PausePlaying();
10477 TapeTogglePause(TAPE_TOGGLE_MANUAL);
10480 case GAME_CTRL_ID_PLAY:
10483 #if defined(PLATFORM_UNIX)
10484 if (options.network)
10485 SendToServer_ContinuePlaying();
10489 tape.pausing = FALSE;
10490 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
10495 case SOUND_CTRL_ID_MUSIC:
10496 if (setup.sound_music)
10498 setup.sound_music = FALSE;
10501 else if (audio.music_available)
10503 setup.sound = setup.sound_music = TRUE;
10505 SetAudioMode(setup.sound);
10511 case SOUND_CTRL_ID_LOOPS:
10512 if (setup.sound_loops)
10513 setup.sound_loops = FALSE;
10514 else if (audio.loops_available)
10516 setup.sound = setup.sound_loops = TRUE;
10517 SetAudioMode(setup.sound);
10521 case SOUND_CTRL_ID_SIMPLE:
10522 if (setup.sound_simple)
10523 setup.sound_simple = FALSE;
10524 else if (audio.sound_available)
10526 setup.sound = setup.sound_simple = TRUE;
10527 SetAudioMode(setup.sound);