1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include "libgame/libgame.h"
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF ( 1)
30 #define USE_NEW_SP_SLIPPERY (USE_NEW_STUFF * 1)
31 #define USE_NEW_CUSTOM_VALUE (USE_NEW_STUFF * 1)
32 #define USE_NEW_PLAYER_ANIM (USE_NEW_STUFF * 1)
33 #define USE_NEW_ALL_SLIPPERY (USE_NEW_STUFF * 1)
34 #define USE_NEW_PLAYER_SPEED (USE_NEW_STUFF * 1)
35 #define USE_NEW_DELAYED_ACTION (USE_NEW_STUFF * 1)
36 #define USE_NEW_SNAP_DELAY (USE_NEW_STUFF * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
39 #define USE_FIXED_DONT_RUN_INTO (USE_NEW_STUFF * 1)
40 #define USE_NEW_SPRING_BUMPER (USE_NEW_STUFF * 1)
41 #define USE_STOP_CHANGED_ELEMENTS (USE_NEW_STUFF * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX (USE_NEW_STUFF * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING (USE_NEW_STUFF * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION (USE_NEW_STUFF * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES (USE_NEW_STUFF * 1)
46 #define USE_PLAYER_GRAVITY (USE_NEW_STUFF * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX (USE_NEW_STUFF * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX (USE_NEW_STUFF * 0)
50 #define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0)
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF * 1)
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX (USE_NEW_STUFF * 1)
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING (USE_NEW_STUFF * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK (USE_NEW_STUFF * 1)
59 #define USE_FIX_KILLED_BY_NON_WALKABLE (USE_NEW_STUFF * 1)
60 #define USE_FIX_IMPACT_COLLISION (USE_NEW_STUFF * 1)
62 #define USE_GFX_RESET_WHEN_NOT_MOVING (USE_NEW_STUFF * 1)
70 /* for MovePlayer() */
71 #define MP_NO_ACTION 0
74 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
76 /* for ScrollPlayer() */
78 #define SCROLL_GO_ON 1
80 /* for Bang()/Explode() */
81 #define EX_PHASE_START 0
82 #define EX_TYPE_NONE 0
83 #define EX_TYPE_NORMAL (1 << 0)
84 #define EX_TYPE_CENTER (1 << 1)
85 #define EX_TYPE_BORDER (1 << 2)
86 #define EX_TYPE_CROSS (1 << 3)
87 #define EX_TYPE_DYNA (1 << 4)
88 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
90 #define PANEL_OFF() (local_player->LevelSolved_PanelOff)
91 #define PANEL_DEACTIVATED(p) ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
92 #define PANEL_XPOS(p) (DX + ALIGNED_TEXT_XPOS(p))
93 #define PANEL_YPOS(p) (DY + ALIGNED_TEXT_YPOS(p))
95 /* special positions in the game control window (relative to control window) */
96 #define XX_LEVEL1 (PANEL_XPOS(game.panel.level))
97 #define XX_LEVEL2 (PANEL_XPOS(game.panel.level) - 1)
98 #define XX_LEVEL (PANEL_XPOS(game.panel.level))
99 #define YY_LEVEL (PANEL_YPOS(game.panel.level))
100 #define XX_EMERALDS (PANEL_XPOS(game.panel.gems))
101 #define YY_EMERALDS (PANEL_YPOS(game.panel.gems))
102 #define XX_DYNAMITE (PANEL_XPOS(game.panel.inventory))
103 #define YY_DYNAMITE (PANEL_YPOS(game.panel.inventory))
104 #define XX_KEYS (PANEL_XPOS(game.panel.keys))
105 #define YY_KEYS (PANEL_YPOS(game.panel.keys))
106 #define XX_SCORE (PANEL_XPOS(game.panel.score))
107 #define YY_SCORE (PANEL_YPOS(game.panel.score))
108 #define XX_TIME1 (PANEL_XPOS(game.panel.time))
109 #define XX_TIME2 (PANEL_XPOS(game.panel.time) + 1)
110 #define XX_TIME (PANEL_XPOS(game.panel.time))
111 #define YY_TIME (PANEL_YPOS(game.panel.time))
113 /* special positions in the game control window (relative to main window) */
114 #define DX_LEVEL1 (DX + XX_LEVEL1)
115 #define DX_LEVEL2 (DX + XX_LEVEL2)
116 #define DX_LEVEL (DX + XX_LEVEL)
117 #define DY_LEVEL (DY + YY_LEVEL)
118 #define DX_EMERALDS (DX + XX_EMERALDS)
119 #define DY_EMERALDS (DY + YY_EMERALDS)
120 #define DX_DYNAMITE (DX + XX_DYNAMITE)
121 #define DY_DYNAMITE (DY + YY_DYNAMITE)
122 #define DX_KEYS (DX + XX_KEYS)
123 #define DY_KEYS (DY + YY_KEYS)
124 #define DX_SCORE (DX + XX_SCORE)
125 #define DY_SCORE (DY + YY_SCORE)
126 #define DX_TIME1 (DX + XX_TIME1)
127 #define DX_TIME2 (DX + XX_TIME2)
128 #define DX_TIME (DX + XX_TIME)
129 #define DY_TIME (DY + YY_TIME)
132 /* game panel display and control definitions */
134 #define GAME_PANEL_LEVEL_NUMBER 0
135 #define GAME_PANEL_GEMS 1
136 #define GAME_PANEL_INVENTORY_COUNT 2
137 #define GAME_PANEL_INVENTORY_FIRST_1 3
138 #define GAME_PANEL_INVENTORY_FIRST_2 4
139 #define GAME_PANEL_INVENTORY_FIRST_3 5
140 #define GAME_PANEL_INVENTORY_FIRST_4 6
141 #define GAME_PANEL_INVENTORY_FIRST_5 7
142 #define GAME_PANEL_INVENTORY_FIRST_6 8
143 #define GAME_PANEL_INVENTORY_FIRST_7 9
144 #define GAME_PANEL_INVENTORY_FIRST_8 10
145 #define GAME_PANEL_INVENTORY_LAST_1 11
146 #define GAME_PANEL_INVENTORY_LAST_2 12
147 #define GAME_PANEL_INVENTORY_LAST_3 13
148 #define GAME_PANEL_INVENTORY_LAST_4 14
149 #define GAME_PANEL_INVENTORY_LAST_5 15
150 #define GAME_PANEL_INVENTORY_LAST_6 16
151 #define GAME_PANEL_INVENTORY_LAST_7 17
152 #define GAME_PANEL_INVENTORY_LAST_8 18
153 #define GAME_PANEL_KEY_1 19
154 #define GAME_PANEL_KEY_2 20
155 #define GAME_PANEL_KEY_3 21
156 #define GAME_PANEL_KEY_4 22
157 #define GAME_PANEL_KEY_5 23
158 #define GAME_PANEL_KEY_6 24
159 #define GAME_PANEL_KEY_7 25
160 #define GAME_PANEL_KEY_8 26
161 #define GAME_PANEL_KEY_WHITE 27
162 #define GAME_PANEL_KEY_WHITE_COUNT 28
163 #define GAME_PANEL_SCORE 29
164 #define GAME_PANEL_HIGHSCORE 30
165 #define GAME_PANEL_TIME 31
166 #define GAME_PANEL_TIME_HH 32
167 #define GAME_PANEL_TIME_MM 33
168 #define GAME_PANEL_TIME_SS 34
169 #define GAME_PANEL_SHIELD_NORMAL 35
170 #define GAME_PANEL_SHIELD_NORMAL_TIME 36
171 #define GAME_PANEL_SHIELD_DEADLY 37
172 #define GAME_PANEL_SHIELD_DEADLY_TIME 38
173 #define GAME_PANEL_EXIT 39
174 #define GAME_PANEL_EMC_MAGIC_BALL 40
175 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH 41
176 #define GAME_PANEL_LIGHT_SWITCH 42
177 #define GAME_PANEL_LIGHT_SWITCH_TIME 43
178 #define GAME_PANEL_TIMEGATE_SWITCH 44
179 #define GAME_PANEL_TIMEGATE_SWITCH_TIME 45
180 #define GAME_PANEL_SWITCHGATE_SWITCH 46
181 #define GAME_PANEL_EMC_LENSES 47
182 #define GAME_PANEL_EMC_LENSES_TIME 48
183 #define GAME_PANEL_EMC_MAGNIFIER 49
184 #define GAME_PANEL_EMC_MAGNIFIER_TIME 50
185 #define GAME_PANEL_BALLOON_SWITCH 51
186 #define GAME_PANEL_DYNABOMB_NUMBER 52
187 #define GAME_PANEL_DYNABOMB_SIZE 53
188 #define GAME_PANEL_DYNABOMB_POWER 54
189 #define GAME_PANEL_PENGUINS 55
190 #define GAME_PANEL_SOKOBAN_OBJECTS 56
191 #define GAME_PANEL_SOKOBAN_FIELDS 57
192 #define GAME_PANEL_ROBOT_WHEEL 58
193 #define GAME_PANEL_CONVEYOR_BELT_1 59
194 #define GAME_PANEL_CONVEYOR_BELT_2 60
195 #define GAME_PANEL_CONVEYOR_BELT_3 61
196 #define GAME_PANEL_CONVEYOR_BELT_4 62
197 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH 63
198 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH 64
199 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH 65
200 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH 66
201 #define GAME_PANEL_MAGIC_WALL 67
202 #define GAME_PANEL_MAGIC_WALL_TIME 68
203 #define GAME_PANEL_GRAVITY_STATE 69
204 #define GAME_PANEL_GRAPHIC_1 70
205 #define GAME_PANEL_GRAPHIC_2 71
206 #define GAME_PANEL_GRAPHIC_3 72
207 #define GAME_PANEL_GRAPHIC_4 73
208 #define GAME_PANEL_GRAPHIC_5 74
209 #define GAME_PANEL_GRAPHIC_6 75
210 #define GAME_PANEL_GRAPHIC_7 76
211 #define GAME_PANEL_GRAPHIC_8 77
212 #define GAME_PANEL_ELEMENT_1 78
213 #define GAME_PANEL_ELEMENT_2 79
214 #define GAME_PANEL_ELEMENT_3 80
215 #define GAME_PANEL_ELEMENT_4 81
216 #define GAME_PANEL_ELEMENT_5 82
217 #define GAME_PANEL_ELEMENT_6 83
218 #define GAME_PANEL_ELEMENT_7 84
219 #define GAME_PANEL_ELEMENT_8 85
220 #define GAME_PANEL_ELEMENT_COUNT_1 86
221 #define GAME_PANEL_ELEMENT_COUNT_2 87
222 #define GAME_PANEL_ELEMENT_COUNT_3 88
223 #define GAME_PANEL_ELEMENT_COUNT_4 89
224 #define GAME_PANEL_ELEMENT_COUNT_5 90
225 #define GAME_PANEL_ELEMENT_COUNT_6 91
226 #define GAME_PANEL_ELEMENT_COUNT_7 92
227 #define GAME_PANEL_ELEMENT_COUNT_8 93
228 #define GAME_PANEL_CE_SCORE_1 94
229 #define GAME_PANEL_CE_SCORE_2 95
230 #define GAME_PANEL_CE_SCORE_3 96
231 #define GAME_PANEL_CE_SCORE_4 97
232 #define GAME_PANEL_CE_SCORE_5 98
233 #define GAME_PANEL_CE_SCORE_6 99
234 #define GAME_PANEL_CE_SCORE_7 100
235 #define GAME_PANEL_CE_SCORE_8 101
236 #define GAME_PANEL_CE_SCORE_1_ELEMENT 102
237 #define GAME_PANEL_CE_SCORE_2_ELEMENT 103
238 #define GAME_PANEL_CE_SCORE_3_ELEMENT 104
239 #define GAME_PANEL_CE_SCORE_4_ELEMENT 105
240 #define GAME_PANEL_CE_SCORE_5_ELEMENT 106
241 #define GAME_PANEL_CE_SCORE_6_ELEMENT 107
242 #define GAME_PANEL_CE_SCORE_7_ELEMENT 108
243 #define GAME_PANEL_CE_SCORE_8_ELEMENT 109
244 #define GAME_PANEL_PLAYER_NAME 110
245 #define GAME_PANEL_LEVEL_NAME 111
246 #define GAME_PANEL_LEVEL_AUTHOR 112
248 #define NUM_GAME_PANEL_CONTROLS 113
250 struct GamePanelOrderInfo
256 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
258 struct GamePanelControlInfo
262 struct TextPosInfo *pos;
265 int value, last_value;
266 int frame, last_frame;
271 static struct GamePanelControlInfo game_panel_controls[] =
274 GAME_PANEL_LEVEL_NUMBER,
275 &game.panel.level_number,
284 GAME_PANEL_INVENTORY_COUNT,
285 &game.panel.inventory_count,
289 GAME_PANEL_INVENTORY_FIRST_1,
290 &game.panel.inventory_first[0],
294 GAME_PANEL_INVENTORY_FIRST_2,
295 &game.panel.inventory_first[1],
299 GAME_PANEL_INVENTORY_FIRST_3,
300 &game.panel.inventory_first[2],
304 GAME_PANEL_INVENTORY_FIRST_4,
305 &game.panel.inventory_first[3],
309 GAME_PANEL_INVENTORY_FIRST_5,
310 &game.panel.inventory_first[4],
314 GAME_PANEL_INVENTORY_FIRST_6,
315 &game.panel.inventory_first[5],
319 GAME_PANEL_INVENTORY_FIRST_7,
320 &game.panel.inventory_first[6],
324 GAME_PANEL_INVENTORY_FIRST_8,
325 &game.panel.inventory_first[7],
329 GAME_PANEL_INVENTORY_LAST_1,
330 &game.panel.inventory_last[0],
334 GAME_PANEL_INVENTORY_LAST_2,
335 &game.panel.inventory_last[1],
339 GAME_PANEL_INVENTORY_LAST_3,
340 &game.panel.inventory_last[2],
344 GAME_PANEL_INVENTORY_LAST_4,
345 &game.panel.inventory_last[3],
349 GAME_PANEL_INVENTORY_LAST_5,
350 &game.panel.inventory_last[4],
354 GAME_PANEL_INVENTORY_LAST_6,
355 &game.panel.inventory_last[5],
359 GAME_PANEL_INVENTORY_LAST_7,
360 &game.panel.inventory_last[6],
364 GAME_PANEL_INVENTORY_LAST_8,
365 &game.panel.inventory_last[7],
409 GAME_PANEL_KEY_WHITE,
410 &game.panel.key_white,
414 GAME_PANEL_KEY_WHITE_COUNT,
415 &game.panel.key_white_count,
424 GAME_PANEL_HIGHSCORE,
425 &game.panel.highscore,
449 GAME_PANEL_SHIELD_NORMAL,
450 &game.panel.shield_normal,
454 GAME_PANEL_SHIELD_NORMAL_TIME,
455 &game.panel.shield_normal_time,
459 GAME_PANEL_SHIELD_DEADLY,
460 &game.panel.shield_deadly,
464 GAME_PANEL_SHIELD_DEADLY_TIME,
465 &game.panel.shield_deadly_time,
474 GAME_PANEL_EMC_MAGIC_BALL,
475 &game.panel.emc_magic_ball,
479 GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
480 &game.panel.emc_magic_ball_switch,
484 GAME_PANEL_LIGHT_SWITCH,
485 &game.panel.light_switch,
489 GAME_PANEL_LIGHT_SWITCH_TIME,
490 &game.panel.light_switch_time,
494 GAME_PANEL_TIMEGATE_SWITCH,
495 &game.panel.timegate_switch,
499 GAME_PANEL_TIMEGATE_SWITCH_TIME,
500 &game.panel.timegate_switch_time,
504 GAME_PANEL_SWITCHGATE_SWITCH,
505 &game.panel.switchgate_switch,
509 GAME_PANEL_EMC_LENSES,
510 &game.panel.emc_lenses,
514 GAME_PANEL_EMC_LENSES_TIME,
515 &game.panel.emc_lenses_time,
519 GAME_PANEL_EMC_MAGNIFIER,
520 &game.panel.emc_magnifier,
524 GAME_PANEL_EMC_MAGNIFIER_TIME,
525 &game.panel.emc_magnifier_time,
529 GAME_PANEL_BALLOON_SWITCH,
530 &game.panel.balloon_switch,
534 GAME_PANEL_DYNABOMB_NUMBER,
535 &game.panel.dynabomb_number,
539 GAME_PANEL_DYNABOMB_SIZE,
540 &game.panel.dynabomb_size,
544 GAME_PANEL_DYNABOMB_POWER,
545 &game.panel.dynabomb_power,
550 &game.panel.penguins,
554 GAME_PANEL_SOKOBAN_OBJECTS,
555 &game.panel.sokoban_objects,
559 GAME_PANEL_SOKOBAN_FIELDS,
560 &game.panel.sokoban_fields,
564 GAME_PANEL_ROBOT_WHEEL,
565 &game.panel.robot_wheel,
569 GAME_PANEL_CONVEYOR_BELT_1,
570 &game.panel.conveyor_belt[0],
574 GAME_PANEL_CONVEYOR_BELT_2,
575 &game.panel.conveyor_belt[1],
579 GAME_PANEL_CONVEYOR_BELT_3,
580 &game.panel.conveyor_belt[2],
584 GAME_PANEL_CONVEYOR_BELT_4,
585 &game.panel.conveyor_belt[3],
589 GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
590 &game.panel.conveyor_belt_switch[0],
594 GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
595 &game.panel.conveyor_belt_switch[1],
599 GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
600 &game.panel.conveyor_belt_switch[2],
604 GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
605 &game.panel.conveyor_belt_switch[3],
609 GAME_PANEL_MAGIC_WALL,
610 &game.panel.magic_wall,
614 GAME_PANEL_MAGIC_WALL_TIME,
615 &game.panel.magic_wall_time,
619 GAME_PANEL_GRAVITY_STATE,
620 &game.panel.gravity_state,
624 GAME_PANEL_GRAPHIC_1,
625 &game.panel.graphic[0],
629 GAME_PANEL_GRAPHIC_2,
630 &game.panel.graphic[1],
634 GAME_PANEL_GRAPHIC_3,
635 &game.panel.graphic[2],
639 GAME_PANEL_GRAPHIC_4,
640 &game.panel.graphic[3],
644 GAME_PANEL_GRAPHIC_5,
645 &game.panel.graphic[4],
649 GAME_PANEL_GRAPHIC_6,
650 &game.panel.graphic[5],
654 GAME_PANEL_GRAPHIC_7,
655 &game.panel.graphic[6],
659 GAME_PANEL_GRAPHIC_8,
660 &game.panel.graphic[7],
664 GAME_PANEL_ELEMENT_1,
665 &game.panel.element[0],
669 GAME_PANEL_ELEMENT_2,
670 &game.panel.element[1],
674 GAME_PANEL_ELEMENT_3,
675 &game.panel.element[2],
679 GAME_PANEL_ELEMENT_4,
680 &game.panel.element[3],
684 GAME_PANEL_ELEMENT_5,
685 &game.panel.element[4],
689 GAME_PANEL_ELEMENT_6,
690 &game.panel.element[5],
694 GAME_PANEL_ELEMENT_7,
695 &game.panel.element[6],
699 GAME_PANEL_ELEMENT_8,
700 &game.panel.element[7],
704 GAME_PANEL_ELEMENT_COUNT_1,
705 &game.panel.element_count[0],
709 GAME_PANEL_ELEMENT_COUNT_2,
710 &game.panel.element_count[1],
714 GAME_PANEL_ELEMENT_COUNT_3,
715 &game.panel.element_count[2],
719 GAME_PANEL_ELEMENT_COUNT_4,
720 &game.panel.element_count[3],
724 GAME_PANEL_ELEMENT_COUNT_5,
725 &game.panel.element_count[4],
729 GAME_PANEL_ELEMENT_COUNT_6,
730 &game.panel.element_count[5],
734 GAME_PANEL_ELEMENT_COUNT_7,
735 &game.panel.element_count[6],
739 GAME_PANEL_ELEMENT_COUNT_8,
740 &game.panel.element_count[7],
744 GAME_PANEL_CE_SCORE_1,
745 &game.panel.ce_score[0],
749 GAME_PANEL_CE_SCORE_2,
750 &game.panel.ce_score[1],
754 GAME_PANEL_CE_SCORE_3,
755 &game.panel.ce_score[2],
759 GAME_PANEL_CE_SCORE_4,
760 &game.panel.ce_score[3],
764 GAME_PANEL_CE_SCORE_5,
765 &game.panel.ce_score[4],
769 GAME_PANEL_CE_SCORE_6,
770 &game.panel.ce_score[5],
774 GAME_PANEL_CE_SCORE_7,
775 &game.panel.ce_score[6],
779 GAME_PANEL_CE_SCORE_8,
780 &game.panel.ce_score[7],
784 GAME_PANEL_CE_SCORE_1_ELEMENT,
785 &game.panel.ce_score_element[0],
789 GAME_PANEL_CE_SCORE_2_ELEMENT,
790 &game.panel.ce_score_element[1],
794 GAME_PANEL_CE_SCORE_3_ELEMENT,
795 &game.panel.ce_score_element[2],
799 GAME_PANEL_CE_SCORE_4_ELEMENT,
800 &game.panel.ce_score_element[3],
804 GAME_PANEL_CE_SCORE_5_ELEMENT,
805 &game.panel.ce_score_element[4],
809 GAME_PANEL_CE_SCORE_6_ELEMENT,
810 &game.panel.ce_score_element[5],
814 GAME_PANEL_CE_SCORE_7_ELEMENT,
815 &game.panel.ce_score_element[6],
819 GAME_PANEL_CE_SCORE_8_ELEMENT,
820 &game.panel.ce_score_element[7],
824 GAME_PANEL_PLAYER_NAME,
825 &game.panel.player_name,
829 GAME_PANEL_LEVEL_NAME,
830 &game.panel.level_name,
834 GAME_PANEL_LEVEL_AUTHOR,
835 &game.panel.level_author,
848 /* values for delayed check of falling and moving elements and for collision */
849 #define CHECK_DELAY_MOVING 3
850 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
851 #define CHECK_DELAY_COLLISION 2
852 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
854 /* values for initial player move delay (initial delay counter value) */
855 #define INITIAL_MOVE_DELAY_OFF -1
856 #define INITIAL_MOVE_DELAY_ON 0
858 /* values for player movement speed (which is in fact a delay value) */
859 #define MOVE_DELAY_MIN_SPEED 32
860 #define MOVE_DELAY_NORMAL_SPEED 8
861 #define MOVE_DELAY_HIGH_SPEED 4
862 #define MOVE_DELAY_MAX_SPEED 1
864 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
865 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
867 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
868 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
870 /* values for other actions */
871 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
872 #define MOVE_STEPSIZE_MIN (1)
873 #define MOVE_STEPSIZE_MAX (TILEX)
875 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
876 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
878 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
880 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
881 RND(element_info[e].push_delay_random))
882 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
883 RND(element_info[e].drop_delay_random))
884 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
885 RND(element_info[e].move_delay_random))
886 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
887 (element_info[e].move_delay_random))
888 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
889 RND(element_info[e].ce_value_random_initial))
890 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
891 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
892 RND((c)->delay_random * (c)->delay_frames))
893 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
894 RND((c)->delay_random))
897 #define GET_VALID_RUNTIME_ELEMENT(e) \
898 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
900 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
901 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
902 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
903 (be) + (e) - EL_SELF)
905 #define GET_PLAYER_FROM_BITS(p) \
906 (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
908 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
909 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
910 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
911 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
912 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
913 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
914 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
915 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
916 RESOLVED_REFERENCE_ELEMENT(be, e) : \
919 #define CAN_GROW_INTO(e) \
920 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
922 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
923 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
927 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
928 (CAN_MOVE_INTO_ACID(e) && \
929 Feld[x][y] == EL_ACID) || \
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
933 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
934 (CAN_MOVE_INTO_ACID(e) && \
935 Feld[x][y] == EL_ACID) || \
938 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
939 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
941 (CAN_MOVE_INTO_ACID(e) && \
942 Feld[x][y] == EL_ACID) || \
943 (DONT_COLLIDE_WITH(e) && \
945 !PLAYER_ENEMY_PROTECTED(x, y))))
947 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
948 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
950 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
951 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
953 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
954 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
956 #define ANDROID_CAN_CLONE_FIELD(x, y) \
957 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
958 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
960 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
961 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
963 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
964 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
966 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
967 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
969 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
970 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
972 #define PIG_CAN_ENTER_FIELD(e, x, y) \
973 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
975 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
976 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
977 Feld[x][y] == EL_EM_EXIT_OPEN || \
978 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
979 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
980 IS_FOOD_PENGUIN(Feld[x][y])))
981 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
982 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
984 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
985 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
987 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
988 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
990 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
991 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
992 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
994 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
996 #define CE_ENTER_FIELD_COND(e, x, y) \
997 (!IS_PLAYER(x, y) && \
998 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
1000 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
1001 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1003 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
1004 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1006 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
1007 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
1008 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
1009 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1011 /* game button identifiers */
1012 #define GAME_CTRL_ID_STOP 0
1013 #define GAME_CTRL_ID_PAUSE 1
1014 #define GAME_CTRL_ID_PLAY 2
1015 #define SOUND_CTRL_ID_MUSIC 3
1016 #define SOUND_CTRL_ID_LOOPS 4
1017 #define SOUND_CTRL_ID_SIMPLE 5
1019 #define NUM_GAME_BUTTONS 6
1022 /* forward declaration for internal use */
1024 static void CreateField(int, int, int);
1026 static void ResetGfxAnimation(int, int);
1028 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1029 static void AdvanceFrameAndPlayerCounters(int);
1031 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1032 static boolean MovePlayer(struct PlayerInfo *, int, int);
1033 static void ScrollPlayer(struct PlayerInfo *, int);
1034 static void ScrollScreen(struct PlayerInfo *, int);
1036 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1038 static void InitBeltMovement(void);
1039 static void CloseAllOpenTimegates(void);
1040 static void CheckGravityMovement(struct PlayerInfo *);
1041 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1042 static void KillPlayerUnlessEnemyProtected(int, int);
1043 static void KillPlayerUnlessExplosionProtected(int, int);
1045 static void TestIfPlayerTouchesCustomElement(int, int);
1046 static void TestIfElementTouchesCustomElement(int, int);
1047 static void TestIfElementHitsCustomElement(int, int, int);
1049 static void TestIfElementSmashesCustomElement(int, int, int);
1052 static void HandleElementChange(int, int, int);
1053 static void ExecuteCustomElementAction(int, int, int, int);
1054 static boolean ChangeElement(int, int, int, int);
1056 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1057 #define CheckTriggeredElementChange(x, y, e, ev) \
1058 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1059 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
1060 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1061 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
1062 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1063 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
1064 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1066 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1067 #define CheckElementChange(x, y, e, te, ev) \
1068 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1069 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
1070 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1071 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
1072 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1074 static void PlayLevelSound(int, int, int);
1075 static void PlayLevelSoundNearest(int, int, int);
1076 static void PlayLevelSoundAction(int, int, int);
1077 static void PlayLevelSoundElementAction(int, int, int, int);
1078 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1079 static void PlayLevelSoundActionIfLoop(int, int, int);
1080 static void StopLevelSoundActionIfLoop(int, int, int);
1081 static void PlayLevelMusic();
1083 static void MapGameButtons();
1084 static void HandleGameButtons(struct GadgetInfo *);
1086 int AmoebeNachbarNr(int, int);
1087 void AmoebeUmwandeln(int, int);
1088 void ContinueMoving(int, int);
1089 void Bang(int, int);
1090 void InitMovDir(int, int);
1091 void InitAmoebaNr(int, int);
1092 int NewHiScore(void);
1094 void TestIfGoodThingHitsBadThing(int, int, int);
1095 void TestIfBadThingHitsGoodThing(int, int, int);
1096 void TestIfPlayerTouchesBadThing(int, int);
1097 void TestIfPlayerRunsIntoBadThing(int, int, int);
1098 void TestIfBadThingTouchesPlayer(int, int);
1099 void TestIfBadThingRunsIntoPlayer(int, int, int);
1100 void TestIfFriendTouchesBadThing(int, int);
1101 void TestIfBadThingTouchesFriend(int, int);
1102 void TestIfBadThingTouchesOtherBadThing(int, int);
1104 void KillPlayer(struct PlayerInfo *);
1105 void BuryPlayer(struct PlayerInfo *);
1106 void RemovePlayer(struct PlayerInfo *);
1108 boolean SnapField(struct PlayerInfo *, int, int);
1109 boolean DropElement(struct PlayerInfo *);
1111 static int getInvisibleActiveFromInvisibleElement(int);
1112 static int getInvisibleFromInvisibleActiveElement(int);
1114 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1116 /* for detection of endless loops, caused by custom element programming */
1117 /* (using maximal playfield width x 10 is just a rough approximation) */
1118 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
1120 #define RECURSION_LOOP_DETECTION_START(e, rc) \
1122 if (recursion_loop_detected) \
1125 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
1127 recursion_loop_detected = TRUE; \
1128 recursion_loop_element = (e); \
1131 recursion_loop_depth++; \
1134 #define RECURSION_LOOP_DETECTION_END() \
1136 recursion_loop_depth--; \
1139 static int recursion_loop_depth;
1140 static boolean recursion_loop_detected;
1141 static boolean recursion_loop_element;
1144 /* ------------------------------------------------------------------------- */
1145 /* definition of elements that automatically change to other elements after */
1146 /* a specified time, eventually calling a function when changing */
1147 /* ------------------------------------------------------------------------- */
1149 /* forward declaration for changer functions */
1150 static void InitBuggyBase(int, int);
1151 static void WarnBuggyBase(int, int);
1153 static void InitTrap(int, int);
1154 static void ActivateTrap(int, int);
1155 static void ChangeActiveTrap(int, int);
1157 static void InitRobotWheel(int, int);
1158 static void RunRobotWheel(int, int);
1159 static void StopRobotWheel(int, int);
1161 static void InitTimegateWheel(int, int);
1162 static void RunTimegateWheel(int, int);
1164 static void InitMagicBallDelay(int, int);
1165 static void ActivateMagicBall(int, int);
1167 struct ChangingElementInfo
1172 void (*pre_change_function)(int x, int y);
1173 void (*change_function)(int x, int y);
1174 void (*post_change_function)(int x, int y);
1177 static struct ChangingElementInfo change_delay_list[] =
1212 EL_STEEL_EXIT_OPENING,
1220 EL_STEEL_EXIT_CLOSING,
1221 EL_STEEL_EXIT_CLOSED,
1248 EL_EM_STEEL_EXIT_OPENING,
1249 EL_EM_STEEL_EXIT_OPEN,
1256 EL_EM_STEEL_EXIT_CLOSING,
1260 EL_EM_STEEL_EXIT_CLOSED,
1284 EL_SWITCHGATE_OPENING,
1292 EL_SWITCHGATE_CLOSING,
1293 EL_SWITCHGATE_CLOSED,
1300 EL_TIMEGATE_OPENING,
1308 EL_TIMEGATE_CLOSING,
1317 EL_ACID_SPLASH_LEFT,
1325 EL_ACID_SPLASH_RIGHT,
1334 EL_SP_BUGGY_BASE_ACTIVATING,
1341 EL_SP_BUGGY_BASE_ACTIVATING,
1342 EL_SP_BUGGY_BASE_ACTIVE,
1349 EL_SP_BUGGY_BASE_ACTIVE,
1373 EL_ROBOT_WHEEL_ACTIVE,
1381 EL_TIMEGATE_SWITCH_ACTIVE,
1389 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1390 EL_DC_TIMEGATE_SWITCH,
1397 EL_EMC_MAGIC_BALL_ACTIVE,
1398 EL_EMC_MAGIC_BALL_ACTIVE,
1405 EL_EMC_SPRING_BUMPER_ACTIVE,
1406 EL_EMC_SPRING_BUMPER,
1413 EL_DIAGONAL_SHRINKING,
1421 EL_DIAGONAL_GROWING,
1442 int push_delay_fixed, push_delay_random;
1446 { EL_SPRING, 0, 0 },
1447 { EL_BALLOON, 0, 0 },
1449 { EL_SOKOBAN_OBJECT, 2, 0 },
1450 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1451 { EL_SATELLITE, 2, 0 },
1452 { EL_SP_DISK_YELLOW, 2, 0 },
1454 { EL_UNDEFINED, 0, 0 },
1462 move_stepsize_list[] =
1464 { EL_AMOEBA_DROP, 2 },
1465 { EL_AMOEBA_DROPPING, 2 },
1466 { EL_QUICKSAND_FILLING, 1 },
1467 { EL_QUICKSAND_EMPTYING, 1 },
1468 { EL_QUICKSAND_FAST_FILLING, 2 },
1469 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1470 { EL_MAGIC_WALL_FILLING, 2 },
1471 { EL_MAGIC_WALL_EMPTYING, 2 },
1472 { EL_BD_MAGIC_WALL_FILLING, 2 },
1473 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1474 { EL_DC_MAGIC_WALL_FILLING, 2 },
1475 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1477 { EL_UNDEFINED, 0 },
1485 collect_count_list[] =
1488 { EL_BD_DIAMOND, 1 },
1489 { EL_EMERALD_YELLOW, 1 },
1490 { EL_EMERALD_RED, 1 },
1491 { EL_EMERALD_PURPLE, 1 },
1493 { EL_SP_INFOTRON, 1 },
1497 { EL_UNDEFINED, 0 },
1505 access_direction_list[] =
1507 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1508 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1509 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1510 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1511 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1512 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1513 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1514 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1515 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1516 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1517 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1519 { EL_SP_PORT_LEFT, MV_RIGHT },
1520 { EL_SP_PORT_RIGHT, MV_LEFT },
1521 { EL_SP_PORT_UP, MV_DOWN },
1522 { EL_SP_PORT_DOWN, MV_UP },
1523 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1524 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1525 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1526 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1527 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1528 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1529 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1530 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1531 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1532 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1533 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1534 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1535 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1536 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1537 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1539 { EL_UNDEFINED, MV_NONE }
1542 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1544 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1545 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1546 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1547 IS_JUST_CHANGING(x, y))
1549 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1551 /* static variables for playfield scan mode (scanning forward or backward) */
1552 static int playfield_scan_start_x = 0;
1553 static int playfield_scan_start_y = 0;
1554 static int playfield_scan_delta_x = 1;
1555 static int playfield_scan_delta_y = 1;
1557 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1558 (y) >= 0 && (y) <= lev_fieldy - 1; \
1559 (y) += playfield_scan_delta_y) \
1560 for ((x) = playfield_scan_start_x; \
1561 (x) >= 0 && (x) <= lev_fieldx - 1; \
1562 (x) += playfield_scan_delta_x)
1565 void DEBUG_SetMaximumDynamite()
1569 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1570 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1571 local_player->inventory_element[local_player->inventory_size++] =
1576 static void InitPlayfieldScanModeVars()
1578 if (game.use_reverse_scan_direction)
1580 playfield_scan_start_x = lev_fieldx - 1;
1581 playfield_scan_start_y = lev_fieldy - 1;
1583 playfield_scan_delta_x = -1;
1584 playfield_scan_delta_y = -1;
1588 playfield_scan_start_x = 0;
1589 playfield_scan_start_y = 0;
1591 playfield_scan_delta_x = 1;
1592 playfield_scan_delta_y = 1;
1596 static void InitPlayfieldScanMode(int mode)
1598 game.use_reverse_scan_direction =
1599 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1601 InitPlayfieldScanModeVars();
1604 static int get_move_delay_from_stepsize(int move_stepsize)
1607 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1609 /* make sure that stepsize value is always a power of 2 */
1610 move_stepsize = (1 << log_2(move_stepsize));
1612 return TILEX / move_stepsize;
1615 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1618 int player_nr = player->index_nr;
1619 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1620 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1622 /* do no immediately change move delay -- the player might just be moving */
1623 player->move_delay_value_next = move_delay;
1625 /* information if player can move must be set separately */
1626 player->cannot_move = cannot_move;
1630 player->move_delay = game.initial_move_delay[player_nr];
1631 player->move_delay_value = game.initial_move_delay_value[player_nr];
1633 player->move_delay_value_next = -1;
1635 player->move_delay_reset_counter = 0;
1639 void GetPlayerConfig()
1641 GameFrameDelay = setup.game_frame_delay;
1643 if (!audio.sound_available)
1644 setup.sound_simple = FALSE;
1646 if (!audio.loops_available)
1647 setup.sound_loops = FALSE;
1649 if (!audio.music_available)
1650 setup.sound_music = FALSE;
1652 if (!video.fullscreen_available)
1653 setup.fullscreen = FALSE;
1655 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1657 SetAudioMode(setup.sound);
1661 int GetElementFromGroupElement(int element)
1663 if (IS_GROUP_ELEMENT(element))
1665 struct ElementGroupInfo *group = element_info[element].group;
1666 int last_anim_random_frame = gfx.anim_random_frame;
1669 if (group->choice_mode == ANIM_RANDOM)
1670 gfx.anim_random_frame = RND(group->num_elements_resolved);
1672 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1673 group->choice_mode, 0,
1676 if (group->choice_mode == ANIM_RANDOM)
1677 gfx.anim_random_frame = last_anim_random_frame;
1679 group->choice_pos++;
1681 element = group->element_resolved[element_pos];
1687 static void InitPlayerField(int x, int y, int element, boolean init_game)
1689 if (element == EL_SP_MURPHY)
1693 if (stored_player[0].present)
1695 Feld[x][y] = EL_SP_MURPHY_CLONE;
1701 stored_player[0].use_murphy = TRUE;
1703 if (!level.use_artwork_element[0])
1704 stored_player[0].artwork_element = EL_SP_MURPHY;
1707 Feld[x][y] = EL_PLAYER_1;
1713 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1714 int jx = player->jx, jy = player->jy;
1716 player->present = TRUE;
1718 player->block_last_field = (element == EL_SP_MURPHY ?
1719 level.sp_block_last_field :
1720 level.block_last_field);
1722 /* ---------- initialize player's last field block delay --------------- */
1724 /* always start with reliable default value (no adjustment needed) */
1725 player->block_delay_adjustment = 0;
1727 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1728 if (player->block_last_field && element == EL_SP_MURPHY)
1729 player->block_delay_adjustment = 1;
1731 /* special case 2: in game engines before 3.1.1, blocking was different */
1732 if (game.use_block_last_field_bug)
1733 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1735 if (!options.network || player->connected)
1737 player->active = TRUE;
1739 /* remove potentially duplicate players */
1740 if (StorePlayer[jx][jy] == Feld[x][y])
1741 StorePlayer[jx][jy] = 0;
1743 StorePlayer[x][y] = Feld[x][y];
1747 printf("Player %d activated.\n", player->element_nr);
1748 printf("[Local player is %d and currently %s.]\n",
1749 local_player->element_nr,
1750 local_player->active ? "active" : "not active");
1754 Feld[x][y] = EL_EMPTY;
1756 player->jx = player->last_jx = x;
1757 player->jy = player->last_jy = y;
1761 static void InitField(int x, int y, boolean init_game)
1763 int element = Feld[x][y];
1772 InitPlayerField(x, y, element, init_game);
1775 case EL_SOKOBAN_FIELD_PLAYER:
1776 element = Feld[x][y] = EL_PLAYER_1;
1777 InitField(x, y, init_game);
1779 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1780 InitField(x, y, init_game);
1783 case EL_SOKOBAN_FIELD_EMPTY:
1784 local_player->sokobanfields_still_needed++;
1788 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1789 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1790 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1791 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1792 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1793 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1794 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1795 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1796 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1797 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1806 case EL_SPACESHIP_RIGHT:
1807 case EL_SPACESHIP_UP:
1808 case EL_SPACESHIP_LEFT:
1809 case EL_SPACESHIP_DOWN:
1810 case EL_BD_BUTTERFLY:
1811 case EL_BD_BUTTERFLY_RIGHT:
1812 case EL_BD_BUTTERFLY_UP:
1813 case EL_BD_BUTTERFLY_LEFT:
1814 case EL_BD_BUTTERFLY_DOWN:
1816 case EL_BD_FIREFLY_RIGHT:
1817 case EL_BD_FIREFLY_UP:
1818 case EL_BD_FIREFLY_LEFT:
1819 case EL_BD_FIREFLY_DOWN:
1820 case EL_PACMAN_RIGHT:
1822 case EL_PACMAN_LEFT:
1823 case EL_PACMAN_DOWN:
1825 case EL_YAMYAM_LEFT:
1826 case EL_YAMYAM_RIGHT:
1828 case EL_YAMYAM_DOWN:
1829 case EL_DARK_YAMYAM:
1832 case EL_SP_SNIKSNAK:
1833 case EL_SP_ELECTRON:
1842 case EL_AMOEBA_FULL:
1847 case EL_AMOEBA_DROP:
1848 if (y == lev_fieldy - 1)
1850 Feld[x][y] = EL_AMOEBA_GROWING;
1851 Store[x][y] = EL_AMOEBA_WET;
1855 case EL_DYNAMITE_ACTIVE:
1856 case EL_SP_DISK_RED_ACTIVE:
1857 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1858 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1859 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1860 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1861 MovDelay[x][y] = 96;
1864 case EL_EM_DYNAMITE_ACTIVE:
1865 MovDelay[x][y] = 32;
1869 local_player->lights_still_needed++;
1873 local_player->friends_still_needed++;
1878 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1881 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1882 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1883 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1884 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1885 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1886 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1887 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1888 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1889 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1890 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1891 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1892 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1895 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1896 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1897 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1899 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1901 game.belt_dir[belt_nr] = belt_dir;
1902 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1904 else /* more than one switch -- set it like the first switch */
1906 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1911 #if !USE_BOTH_SWITCHGATE_SWITCHES
1912 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1914 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1917 case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1919 Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1923 case EL_LIGHT_SWITCH_ACTIVE:
1925 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1928 case EL_INVISIBLE_STEELWALL:
1929 case EL_INVISIBLE_WALL:
1930 case EL_INVISIBLE_SAND:
1931 if (game.light_time_left > 0 ||
1932 game.lenses_time_left > 0)
1933 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1936 case EL_EMC_MAGIC_BALL:
1937 if (game.ball_state)
1938 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1941 case EL_EMC_MAGIC_BALL_SWITCH:
1942 if (game.ball_state)
1943 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1947 if (IS_CUSTOM_ELEMENT(element))
1949 if (CAN_MOVE(element))
1952 #if USE_NEW_CUSTOM_VALUE
1953 if (!element_info[element].use_last_ce_value || init_game)
1954 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1957 else if (IS_GROUP_ELEMENT(element))
1959 Feld[x][y] = GetElementFromGroupElement(element);
1961 InitField(x, y, init_game);
1968 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1971 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1973 InitField(x, y, init_game);
1975 /* not needed to call InitMovDir() -- already done by InitField()! */
1976 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1977 CAN_MOVE(Feld[x][y]))
1981 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1983 int old_element = Feld[x][y];
1985 InitField(x, y, init_game);
1987 /* not needed to call InitMovDir() -- already done by InitField()! */
1988 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1989 CAN_MOVE(old_element) &&
1990 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1993 /* this case is in fact a combination of not less than three bugs:
1994 first, it calls InitMovDir() for elements that can move, although this is
1995 already done by InitField(); then, it checks the element that was at this
1996 field _before_ the call to InitField() (which can change it); lastly, it
1997 was not called for "mole with direction" elements, which were treated as
1998 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2004 static int get_key_element_from_nr(int key_nr)
2006 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2007 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2008 EL_EM_KEY_1 : EL_KEY_1);
2010 return key_base_element + key_nr;
2013 static int get_next_dropped_element(struct PlayerInfo *player)
2015 return (player->inventory_size > 0 ?
2016 player->inventory_element[player->inventory_size - 1] :
2017 player->inventory_infinite_element != EL_UNDEFINED ?
2018 player->inventory_infinite_element :
2019 player->dynabombs_left > 0 ?
2020 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2024 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2026 /* pos >= 0: get element from bottom of the stack;
2027 pos < 0: get element from top of the stack */
2031 int min_inventory_size = -pos;
2032 int inventory_pos = player->inventory_size - min_inventory_size;
2033 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2035 return (player->inventory_size >= min_inventory_size ?
2036 player->inventory_element[inventory_pos] :
2037 player->inventory_infinite_element != EL_UNDEFINED ?
2038 player->inventory_infinite_element :
2039 player->dynabombs_left >= min_dynabombs_left ?
2040 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2045 int min_dynabombs_left = pos + 1;
2046 int min_inventory_size = pos + 1 - player->dynabombs_left;
2047 int inventory_pos = pos - player->dynabombs_left;
2049 return (player->inventory_infinite_element != EL_UNDEFINED ?
2050 player->inventory_infinite_element :
2051 player->dynabombs_left >= min_dynabombs_left ?
2052 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2053 player->inventory_size >= min_inventory_size ?
2054 player->inventory_element[inventory_pos] :
2059 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2061 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2062 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2065 if (gpo1->sort_priority != gpo2->sort_priority)
2066 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2068 compare_result = gpo1->nr - gpo2->nr;
2070 return compare_result;
2073 void InitGameControlValues()
2077 for (i = 0; game_panel_controls[i].nr != -1; i++)
2079 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2080 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2081 struct TextPosInfo *pos = gpc->pos;
2083 int type = gpc->type;
2087 Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2088 Error(ERR_EXIT, "this should not happen -- please debug");
2091 /* force update of game controls after initialization */
2092 gpc->value = gpc->last_value = -1;
2093 gpc->frame = gpc->last_frame = -1;
2094 gpc->gfx_frame = -1;
2096 /* determine panel value width for later calculation of alignment */
2097 if (type == TYPE_INTEGER || type == TYPE_STRING)
2099 pos->width = pos->size * getFontWidth(pos->font);
2100 pos->height = getFontHeight(pos->font);
2102 else if (type == TYPE_ELEMENT)
2104 pos->width = pos->size;
2105 pos->height = pos->size;
2108 /* fill structure for game panel draw order */
2110 gpo->sort_priority = pos->sort_priority;
2113 /* sort game panel controls according to sort_priority and control number */
2114 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2115 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2118 void UpdatePlayfieldElementCount()
2120 boolean use_element_count = FALSE;
2123 /* first check if it is needed at all to calculate playfield element count */
2124 for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2125 if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2126 use_element_count = TRUE;
2128 if (!use_element_count)
2131 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2132 element_info[i].element_count = 0;
2134 SCAN_PLAYFIELD(x, y)
2136 element_info[Feld[x][y]].element_count++;
2139 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2140 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2141 if (IS_IN_GROUP(j, i))
2142 element_info[EL_GROUP_START + i].element_count +=
2143 element_info[j].element_count;
2146 void UpdateGameControlValues()
2149 int time = (local_player->LevelSolved ?
2150 local_player->LevelSolved_CountingTime :
2151 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2152 level.native_em_level->lev->time :
2153 level.time == 0 ? TimePlayed : TimeLeft);
2154 int score = (local_player->LevelSolved ?
2155 local_player->LevelSolved_CountingScore :
2156 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2157 level.native_em_level->lev->score :
2158 local_player->score);
2159 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2160 level.native_em_level->lev->required :
2161 local_player->gems_still_needed);
2162 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2163 level.native_em_level->lev->required > 0 :
2164 local_player->gems_still_needed > 0 ||
2165 local_player->sokobanfields_still_needed > 0 ||
2166 local_player->lights_still_needed > 0);
2168 UpdatePlayfieldElementCount();
2170 /* update game panel control values */
2172 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2173 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2175 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2176 for (i = 0; i < MAX_NUM_KEYS; i++)
2177 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2178 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2179 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2181 if (game.centered_player_nr == -1)
2183 for (i = 0; i < MAX_PLAYERS; i++)
2185 for (k = 0; k < MAX_NUM_KEYS; k++)
2187 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2189 if (level.native_em_level->ply[i]->keys & (1 << k))
2190 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2191 get_key_element_from_nr(k);
2193 else if (stored_player[i].key[k])
2194 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2195 get_key_element_from_nr(k);
2198 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2199 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2200 level.native_em_level->ply[i]->dynamite;
2202 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2203 stored_player[i].inventory_size;
2205 if (stored_player[i].num_white_keys > 0)
2206 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2209 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2210 stored_player[i].num_white_keys;
2215 int player_nr = game.centered_player_nr;
2217 for (k = 0; k < MAX_NUM_KEYS; k++)
2219 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2221 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2222 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2223 get_key_element_from_nr(k);
2225 else if (stored_player[player_nr].key[k])
2226 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2227 get_key_element_from_nr(k);
2230 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2231 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2232 level.native_em_level->ply[player_nr]->dynamite;
2234 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2235 stored_player[player_nr].inventory_size;
2237 if (stored_player[player_nr].num_white_keys > 0)
2238 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2240 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2241 stored_player[player_nr].num_white_keys;
2244 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2246 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2247 get_inventory_element_from_pos(local_player, i);
2248 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2249 get_inventory_element_from_pos(local_player, -i - 1);
2252 game_panel_controls[GAME_PANEL_SCORE].value = score;
2253 game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2255 game_panel_controls[GAME_PANEL_TIME].value = time;
2257 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2258 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2259 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2261 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2262 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2264 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2265 local_player->shield_normal_time_left;
2266 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2267 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2269 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2270 local_player->shield_deadly_time_left;
2272 game_panel_controls[GAME_PANEL_EXIT].value =
2273 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2275 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2276 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2277 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2278 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2279 EL_EMC_MAGIC_BALL_SWITCH);
2281 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2282 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2283 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2284 game.light_time_left;
2286 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2287 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2288 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2289 game.timegate_time_left;
2291 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2292 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2294 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2295 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2296 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2297 game.lenses_time_left;
2299 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2300 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2301 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2302 game.magnify_time_left;
2304 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2305 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2306 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2307 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2308 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2309 EL_BALLOON_SWITCH_NONE);
2311 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2312 local_player->dynabomb_count;
2313 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2314 local_player->dynabomb_size;
2315 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2316 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2318 game_panel_controls[GAME_PANEL_PENGUINS].value =
2319 local_player->friends_still_needed;
2321 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2322 local_player->sokobanfields_still_needed;
2323 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2324 local_player->sokobanfields_still_needed;
2326 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2327 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2329 for (i = 0; i < NUM_BELTS; i++)
2331 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2332 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2333 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2334 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2335 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2338 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2339 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2340 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2341 game.magic_wall_time_left;
2343 #if USE_PLAYER_GRAVITY
2344 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2345 local_player->gravity;
2347 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2350 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2351 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2353 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2354 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2355 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2356 game.panel.element[i].id : EL_UNDEFINED);
2358 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2359 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2360 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2361 element_info[game.panel.element_count[i].id].element_count : 0);
2363 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2364 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2365 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2366 element_info[game.panel.ce_score[i].id].collect_score : 0);
2368 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2369 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2370 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2371 element_info[game.panel.ce_score_element[i].id].collect_score :
2374 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2375 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2376 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2378 /* update game panel control frames */
2380 for (i = 0; game_panel_controls[i].nr != -1; i++)
2382 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2384 if (gpc->type == TYPE_ELEMENT)
2386 int last_anim_random_frame = gfx.anim_random_frame;
2387 int element = gpc->value;
2388 int graphic = el2panelimg(element);
2390 if (gpc->value != gpc->last_value)
2393 gpc->gfx_random = INIT_GFX_RANDOM();
2399 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2400 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2401 gpc->gfx_random = INIT_GFX_RANDOM();
2404 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2405 gfx.anim_random_frame = gpc->gfx_random;
2407 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2408 gpc->gfx_frame = element_info[element].collect_score;
2410 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2413 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2414 gfx.anim_random_frame = last_anim_random_frame;
2419 void DisplayGameControlValues()
2421 boolean redraw_panel = FALSE;
2424 for (i = 0; game_panel_controls[i].nr != -1; i++)
2426 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2428 if (PANEL_DEACTIVATED(gpc->pos))
2431 if (gpc->value == gpc->last_value &&
2432 gpc->frame == gpc->last_frame)
2435 redraw_panel = TRUE;
2441 /* copy default game door content to main double buffer */
2442 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2443 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2445 /* redraw game control buttons */
2447 RedrawGameButtons();
2453 game_status = GAME_MODE_PSEUDO_PANEL;
2456 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2458 for (i = 0; game_panel_controls[i].nr != -1; i++)
2462 int nr = game_panel_order[i].nr;
2463 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2465 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2468 struct TextPosInfo *pos = gpc->pos;
2469 int type = gpc->type;
2470 int value = gpc->value;
2471 int frame = gpc->frame;
2473 int last_value = gpc->last_value;
2474 int last_frame = gpc->last_frame;
2476 int size = pos->size;
2477 int font = pos->font;
2478 boolean draw_masked = pos->draw_masked;
2479 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2481 if (PANEL_DEACTIVATED(pos))
2485 if (value == last_value && frame == last_frame)
2489 gpc->last_value = value;
2490 gpc->last_frame = frame;
2493 printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2496 if (type == TYPE_INTEGER)
2498 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2499 nr == GAME_PANEL_TIME)
2501 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2503 if (use_dynamic_size) /* use dynamic number of digits */
2505 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2506 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2507 int size2 = size1 + 1;
2508 int font1 = pos->font;
2509 int font2 = pos->font_alt;
2511 size = (value < value_change ? size1 : size2);
2512 font = (value < value_change ? font1 : font2);
2515 /* clear background if value just changed its size (dynamic digits) */
2516 if ((last_value < value_change) != (value < value_change))
2518 int width1 = size1 * getFontWidth(font1);
2519 int width2 = size2 * getFontWidth(font2);
2520 int max_width = MAX(width1, width2);
2521 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2523 pos->width = max_width;
2525 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2526 max_width, max_height);
2533 /* correct text size if "digits" is zero or less */
2535 size = strlen(int2str(value, size));
2537 /* dynamically correct text alignment */
2538 pos->width = size * getFontWidth(font);
2541 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2542 int2str(value, size), font, mask_mode);
2544 else if (type == TYPE_ELEMENT)
2546 int element, graphic;
2550 int dst_x = PANEL_XPOS(pos);
2551 int dst_y = PANEL_YPOS(pos);
2554 if (value != EL_UNDEFINED && value != EL_EMPTY)
2557 graphic = el2panelimg(value);
2559 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2562 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2566 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2569 width = graphic_info[graphic].width * size / TILESIZE;
2570 height = graphic_info[graphic].height * size / TILESIZE;
2574 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2575 dst_x - src_x, dst_y - src_y);
2576 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2581 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2586 if (value == EL_UNDEFINED || value == EL_EMPTY)
2588 element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2589 graphic = el2panelimg(element);
2591 src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2592 src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2593 src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2598 graphic = el2panelimg(value);
2600 getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2603 width = graphic_info[graphic].width * size / TILESIZE;
2604 height = graphic_info[graphic].height * size / TILESIZE;
2606 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2609 else if (type == TYPE_STRING)
2611 boolean active = (value != 0);
2612 char *state_normal = "off";
2613 char *state_active = "on";
2614 char *state = (active ? state_active : state_normal);
2615 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2616 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2617 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2618 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2620 if (nr == GAME_PANEL_GRAVITY_STATE)
2622 int font1 = pos->font; /* (used for normal state) */
2623 int font2 = pos->font_alt; /* (used for active state) */
2625 int size1 = strlen(state_normal);
2626 int size2 = strlen(state_active);
2627 int width1 = size1 * getFontWidth(font1);
2628 int width2 = size2 * getFontWidth(font2);
2629 int max_width = MAX(width1, width2);
2630 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2632 pos->width = max_width;
2634 /* clear background for values that may have changed its size */
2635 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2636 max_width, max_height);
2639 font = (active ? font2 : font1);
2649 /* don't truncate output if "chars" is zero or less */
2652 /* dynamically correct text alignment */
2653 pos->width = size * getFontWidth(font);
2657 s_cut = getStringCopyN(s, size);
2659 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2660 s_cut, font, mask_mode);
2666 redraw_mask |= REDRAW_DOOR_1;
2669 game_status = GAME_MODE_PLAYING;
2672 void UpdateAndDisplayGameControlValues()
2674 if (tape.warp_forward)
2677 UpdateGameControlValues();
2678 DisplayGameControlValues();
2681 void DrawGameValue_Emeralds(int value)
2683 struct TextPosInfo *pos = &game.panel.gems;
2685 int font_nr = pos->font;
2687 int font_nr = FONT_TEXT_2;
2689 int font_width = getFontWidth(font_nr);
2690 int chars = pos->size;
2693 return; /* !!! USE NEW STUFF !!! */
2696 if (PANEL_DEACTIVATED(pos))
2699 pos->width = chars * font_width;
2701 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2704 void DrawGameValue_Dynamite(int value)
2706 struct TextPosInfo *pos = &game.panel.inventory_count;
2708 int font_nr = pos->font;
2710 int font_nr = FONT_TEXT_2;
2712 int font_width = getFontWidth(font_nr);
2713 int chars = pos->size;
2716 return; /* !!! USE NEW STUFF !!! */
2719 if (PANEL_DEACTIVATED(pos))
2722 pos->width = chars * font_width;
2724 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2727 void DrawGameValue_Score(int value)
2729 struct TextPosInfo *pos = &game.panel.score;
2731 int font_nr = pos->font;
2733 int font_nr = FONT_TEXT_2;
2735 int font_width = getFontWidth(font_nr);
2736 int chars = pos->size;
2739 return; /* !!! USE NEW STUFF !!! */
2742 if (PANEL_DEACTIVATED(pos))
2745 pos->width = chars * font_width;
2747 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2750 void DrawGameValue_Time(int value)
2752 struct TextPosInfo *pos = &game.panel.time;
2753 static int last_value = -1;
2756 int chars = pos->size;
2758 int font1_nr = pos->font;
2759 int font2_nr = pos->font_alt;
2761 int font1_nr = FONT_TEXT_2;
2762 int font2_nr = FONT_TEXT_1;
2764 int font_nr = font1_nr;
2765 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2768 return; /* !!! USE NEW STUFF !!! */
2771 if (PANEL_DEACTIVATED(pos))
2774 if (use_dynamic_chars) /* use dynamic number of chars */
2776 chars = (value < 1000 ? chars1 : chars2);
2777 font_nr = (value < 1000 ? font1_nr : font2_nr);
2780 /* clear background if value just changed its size (dynamic chars only) */
2781 if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2783 int width1 = chars1 * getFontWidth(font1_nr);
2784 int width2 = chars2 * getFontWidth(font2_nr);
2785 int max_width = MAX(width1, width2);
2786 int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2788 pos->width = max_width;
2790 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2791 max_width, max_height);
2794 pos->width = chars * getFontWidth(font_nr);
2796 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2801 void DrawGameValue_Level(int value)
2803 struct TextPosInfo *pos = &game.panel.level_number;
2806 int chars = pos->size;
2808 int font1_nr = pos->font;
2809 int font2_nr = pos->font_alt;
2811 int font1_nr = FONT_TEXT_2;
2812 int font2_nr = FONT_TEXT_1;
2814 int font_nr = font1_nr;
2815 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2818 return; /* !!! USE NEW STUFF !!! */
2821 if (PANEL_DEACTIVATED(pos))
2824 if (use_dynamic_chars) /* use dynamic number of chars */
2826 chars = (level_nr < 100 ? chars1 : chars2);
2827 font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2830 pos->width = chars * getFontWidth(font_nr);
2832 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2835 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2838 struct TextPosInfo *pos = &game.panel.keys;
2841 int base_key_graphic = EL_KEY_1;
2846 return; /* !!! USE NEW STUFF !!! */
2850 if (PANEL_DEACTIVATED(pos))
2855 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2856 base_key_graphic = EL_EM_KEY_1;
2860 pos->width = 4 * MINI_TILEX;
2864 for (i = 0; i < MAX_NUM_KEYS; i++)
2866 /* currently only 4 of 8 possible keys are displayed */
2867 for (i = 0; i < STD_NUM_KEYS; i++)
2871 struct TextPosInfo *pos = &game.panel.key[i];
2873 int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2874 int src_y = DOOR_GFX_PAGEY1 + 123;
2876 int dst_x = PANEL_XPOS(pos);
2877 int dst_y = PANEL_YPOS(pos);
2879 int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2880 int dst_y = PANEL_YPOS(pos);
2884 int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2885 level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2887 int graphic = el2edimg(element);
2891 if (PANEL_DEACTIVATED(pos))
2896 /* masked blit with tiles from half-size scaled bitmap does not work yet
2897 (no mask bitmap created for these sizes after loading and scaling) --
2898 solution: load without creating mask, scale, then create final mask */
2900 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2901 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2906 int graphic = el2edimg(base_key_graphic + i);
2911 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2913 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2914 dst_x - src_x, dst_y - src_y);
2915 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2921 DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2923 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2924 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2927 DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
2929 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2930 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2938 void DrawGameValue_Emeralds(int value)
2940 int font_nr = FONT_TEXT_2;
2941 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2943 if (PANEL_DEACTIVATED(game.panel.gems))
2946 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
2949 void DrawGameValue_Dynamite(int value)
2951 int font_nr = FONT_TEXT_2;
2952 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2954 if (PANEL_DEACTIVATED(game.panel.inventory_count))
2957 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
2960 void DrawGameValue_Score(int value)
2962 int font_nr = FONT_TEXT_2;
2963 int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
2965 if (PANEL_DEACTIVATED(game.panel.score))
2968 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
2971 void DrawGameValue_Time(int value)
2973 int font1_nr = FONT_TEXT_2;
2975 int font2_nr = FONT_TEXT_1;
2977 int font2_nr = FONT_LEVEL_NUMBER;
2979 int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
2980 int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
2982 if (PANEL_DEACTIVATED(game.panel.time))
2985 /* clear background if value just changed its size */
2986 if (value == 999 || value == 1000)
2987 ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
2990 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
2992 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
2995 void DrawGameValue_Level(int value)
2997 int font1_nr = FONT_TEXT_2;
2999 int font2_nr = FONT_TEXT_1;
3001 int font2_nr = FONT_LEVEL_NUMBER;
3004 if (PANEL_DEACTIVATED(game.panel.level))
3008 DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
3010 DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
3013 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
3015 int base_key_graphic = EL_KEY_1;
3018 if (PANEL_DEACTIVATED(game.panel.keys))
3021 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3022 base_key_graphic = EL_EM_KEY_1;
3024 /* currently only 4 of 8 possible keys are displayed */
3025 for (i = 0; i < STD_NUM_KEYS; i++)
3027 int x = XX_KEYS + i * MINI_TILEX;
3031 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3033 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3034 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3040 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3043 int key[MAX_NUM_KEYS];
3046 /* prevent EM engine from updating time/score values parallel to GameWon() */
3047 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3048 local_player->LevelSolved)
3051 for (i = 0; i < MAX_NUM_KEYS; i++)
3052 key[i] = key_bits & (1 << i);
3054 DrawGameValue_Level(level_nr);
3056 DrawGameValue_Emeralds(emeralds);
3057 DrawGameValue_Dynamite(dynamite);
3058 DrawGameValue_Score(score);
3059 DrawGameValue_Time(time);
3061 DrawGameValue_Keys(key);
3064 void UpdateGameDoorValues()
3066 UpdateGameControlValues();
3069 void DrawGameDoorValues()
3071 DisplayGameControlValues();
3074 void DrawGameDoorValues_OLD()
3076 int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
3077 int dynamite_value = 0;
3078 int score_value = (local_player->LevelSolved ? local_player->score_final :
3079 local_player->score);
3080 int gems_value = local_player->gems_still_needed;
3084 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3086 DrawGameDoorValues_EM();
3091 if (game.centered_player_nr == -1)
3093 for (i = 0; i < MAX_PLAYERS; i++)
3095 for (j = 0; j < MAX_NUM_KEYS; j++)
3096 if (stored_player[i].key[j])
3097 key_bits |= (1 << j);
3099 dynamite_value += stored_player[i].inventory_size;
3104 int player_nr = game.centered_player_nr;
3106 for (i = 0; i < MAX_NUM_KEYS; i++)
3107 if (stored_player[player_nr].key[i])
3108 key_bits |= (1 << i);
3110 dynamite_value = stored_player[player_nr].inventory_size;
3113 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3119 =============================================================================
3121 -----------------------------------------------------------------------------
3122 initialize game engine due to level / tape version number
3123 =============================================================================
3126 static void InitGameEngine()
3128 int i, j, k, l, x, y;
3130 /* set game engine from tape file when re-playing, else from level file */
3131 game.engine_version = (tape.playing ? tape.engine_version :
3132 level.game_version);
3134 /* ---------------------------------------------------------------------- */
3135 /* set flags for bugs and changes according to active game engine version */
3136 /* ---------------------------------------------------------------------- */
3139 Summary of bugfix/change:
3140 Fixed handling for custom elements that change when pushed by the player.
3142 Fixed/changed in version:
3146 Before 3.1.0, custom elements that "change when pushing" changed directly
3147 after the player started pushing them (until then handled in "DigField()").
3148 Since 3.1.0, these custom elements are not changed until the "pushing"
3149 move of the element is finished (now handled in "ContinueMoving()").
3151 Affected levels/tapes:
3152 The first condition is generally needed for all levels/tapes before version
3153 3.1.0, which might use the old behaviour before it was changed; known tapes
3154 that are affected are some tapes from the level set "Walpurgis Gardens" by
3156 The second condition is an exception from the above case and is needed for
3157 the special case of tapes recorded with game (not engine!) version 3.1.0 or
3158 above (including some development versions of 3.1.0), but before it was
3159 known that this change would break tapes like the above and was fixed in
3160 3.1.1, so that the changed behaviour was active although the engine version
3161 while recording maybe was before 3.1.0. There is at least one tape that is
3162 affected by this exception, which is the tape for the one-level set "Bug
3163 Machine" by Juergen Bonhagen.
3166 game.use_change_when_pushing_bug =
3167 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3169 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3170 tape.game_version < VERSION_IDENT(3,1,1,0)));
3173 Summary of bugfix/change:
3174 Fixed handling for blocking the field the player leaves when moving.
3176 Fixed/changed in version:
3180 Before 3.1.1, when "block last field when moving" was enabled, the field
3181 the player is leaving when moving was blocked for the time of the move,
3182 and was directly unblocked afterwards. This resulted in the last field
3183 being blocked for exactly one less than the number of frames of one player
3184 move. Additionally, even when blocking was disabled, the last field was
3185 blocked for exactly one frame.
3186 Since 3.1.1, due to changes in player movement handling, the last field
3187 is not blocked at all when blocking is disabled. When blocking is enabled,
3188 the last field is blocked for exactly the number of frames of one player
3189 move. Additionally, if the player is Murphy, the hero of Supaplex, the
3190 last field is blocked for exactly one more than the number of frames of
3193 Affected levels/tapes:
3194 (!!! yet to be determined -- probably many !!!)
3197 game.use_block_last_field_bug =
3198 (game.engine_version < VERSION_IDENT(3,1,1,0));
3201 Summary of bugfix/change:
3202 Changed behaviour of CE changes with multiple changes per single frame.
3204 Fixed/changed in version:
3208 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3209 This resulted in race conditions where CEs seem to behave strange in some
3210 situations (where triggered CE changes were just skipped because there was
3211 already a CE change on that tile in the playfield in that engine frame).
3212 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3213 (The number of changes per frame must be limited in any case, because else
3214 it is easily possible to define CE changes that would result in an infinite
3215 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3216 should be set large enough so that it would only be reached in cases where
3217 the corresponding CE change conditions run into a loop. Therefore, it seems
3218 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3219 maximal number of change pages for custom elements.)
3221 Affected levels/tapes:
3225 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3226 game.max_num_changes_per_frame = 1;
3228 game.max_num_changes_per_frame =
3229 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3232 /* ---------------------------------------------------------------------- */
3234 /* default scan direction: scan playfield from top/left to bottom/right */
3235 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3237 /* dynamically adjust element properties according to game engine version */
3238 InitElementPropertiesEngine(game.engine_version);
3241 printf("level %d: level version == %06d\n", level_nr, level.game_version);
3242 printf(" tape version == %06d [%s] [file: %06d]\n",
3243 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3245 printf(" => game.engine_version == %06d\n", game.engine_version);
3248 /* ---------- initialize player's initial move delay --------------------- */
3250 /* dynamically adjust player properties according to level information */
3251 for (i = 0; i < MAX_PLAYERS; i++)
3252 game.initial_move_delay_value[i] =
3253 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3255 /* dynamically adjust player properties according to game engine version */
3256 for (i = 0; i < MAX_PLAYERS; i++)
3257 game.initial_move_delay[i] =
3258 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3259 game.initial_move_delay_value[i] : 0);
3261 /* ---------- initialize player's initial push delay --------------------- */
3263 /* dynamically adjust player properties according to game engine version */
3264 game.initial_push_delay_value =
3265 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3267 /* ---------- initialize changing elements ------------------------------- */
3269 /* initialize changing elements information */
3270 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3272 struct ElementInfo *ei = &element_info[i];
3274 /* this pointer might have been changed in the level editor */
3275 ei->change = &ei->change_page[0];
3277 if (!IS_CUSTOM_ELEMENT(i))
3279 ei->change->target_element = EL_EMPTY_SPACE;
3280 ei->change->delay_fixed = 0;
3281 ei->change->delay_random = 0;
3282 ei->change->delay_frames = 1;
3285 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3287 ei->has_change_event[j] = FALSE;
3289 ei->event_page_nr[j] = 0;
3290 ei->event_page[j] = &ei->change_page[0];
3294 /* add changing elements from pre-defined list */
3295 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3297 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3298 struct ElementInfo *ei = &element_info[ch_delay->element];
3300 ei->change->target_element = ch_delay->target_element;
3301 ei->change->delay_fixed = ch_delay->change_delay;
3303 ei->change->pre_change_function = ch_delay->pre_change_function;
3304 ei->change->change_function = ch_delay->change_function;
3305 ei->change->post_change_function = ch_delay->post_change_function;
3307 ei->change->can_change = TRUE;
3308 ei->change->can_change_or_has_action = TRUE;
3310 ei->has_change_event[CE_DELAY] = TRUE;
3312 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3313 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3316 /* ---------- initialize internal run-time variables ------------- */
3318 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3320 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3322 for (j = 0; j < ei->num_change_pages; j++)
3324 ei->change_page[j].can_change_or_has_action =
3325 (ei->change_page[j].can_change |
3326 ei->change_page[j].has_action);
3330 /* add change events from custom element configuration */
3331 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3333 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3335 for (j = 0; j < ei->num_change_pages; j++)
3337 if (!ei->change_page[j].can_change_or_has_action)
3340 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3342 /* only add event page for the first page found with this event */
3343 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3345 ei->has_change_event[k] = TRUE;
3347 ei->event_page_nr[k] = j;
3348 ei->event_page[k] = &ei->change_page[j];
3354 /* ---------- initialize run-time trigger player and element ------------- */
3356 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3358 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3360 for (j = 0; j < ei->num_change_pages; j++)
3362 ei->change_page[j].actual_trigger_element = EL_EMPTY;
3363 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
3364 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_1;
3365 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3366 ei->change_page[j].actual_trigger_ce_value = 0;
3367 ei->change_page[j].actual_trigger_ce_score = 0;
3371 /* ---------- initialize trigger events ---------------------------------- */
3373 /* initialize trigger events information */
3374 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3375 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3376 trigger_events[i][j] = FALSE;
3378 /* add trigger events from element change event properties */
3379 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3381 struct ElementInfo *ei = &element_info[i];
3383 for (j = 0; j < ei->num_change_pages; j++)
3385 if (!ei->change_page[j].can_change_or_has_action)
3388 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3390 int trigger_element = ei->change_page[j].trigger_element;
3392 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3394 if (ei->change_page[j].has_event[k])
3396 if (IS_GROUP_ELEMENT(trigger_element))
3398 struct ElementGroupInfo *group =
3399 element_info[trigger_element].group;
3401 for (l = 0; l < group->num_elements_resolved; l++)
3402 trigger_events[group->element_resolved[l]][k] = TRUE;
3404 else if (trigger_element == EL_ANY_ELEMENT)
3405 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3406 trigger_events[l][k] = TRUE;
3408 trigger_events[trigger_element][k] = TRUE;
3415 /* ---------- initialize push delay -------------------------------------- */
3417 /* initialize push delay values to default */
3418 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3420 if (!IS_CUSTOM_ELEMENT(i))
3422 /* set default push delay values (corrected since version 3.0.7-1) */
3423 if (game.engine_version < VERSION_IDENT(3,0,7,1))
3425 element_info[i].push_delay_fixed = 2;
3426 element_info[i].push_delay_random = 8;
3430 element_info[i].push_delay_fixed = 8;
3431 element_info[i].push_delay_random = 8;
3436 /* set push delay value for certain elements from pre-defined list */
3437 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3439 int e = push_delay_list[i].element;
3441 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
3442 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3445 /* set push delay value for Supaplex elements for newer engine versions */
3446 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3448 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3450 if (IS_SP_ELEMENT(i))
3452 /* set SP push delay to just enough to push under a falling zonk */
3453 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3455 element_info[i].push_delay_fixed = delay;
3456 element_info[i].push_delay_random = 0;
3461 /* ---------- initialize move stepsize ----------------------------------- */
3463 /* initialize move stepsize values to default */
3464 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3465 if (!IS_CUSTOM_ELEMENT(i))
3466 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3468 /* set move stepsize value for certain elements from pre-defined list */
3469 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3471 int e = move_stepsize_list[i].element;
3473 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3476 /* ---------- initialize collect score ----------------------------------- */
3478 /* initialize collect score values for custom elements from initial value */
3479 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3480 if (IS_CUSTOM_ELEMENT(i))
3481 element_info[i].collect_score = element_info[i].collect_score_initial;
3483 /* ---------- initialize collect count ----------------------------------- */
3485 /* initialize collect count values for non-custom elements */
3486 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3487 if (!IS_CUSTOM_ELEMENT(i))
3488 element_info[i].collect_count_initial = 0;
3490 /* add collect count values for all elements from pre-defined list */
3491 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3492 element_info[collect_count_list[i].element].collect_count_initial =
3493 collect_count_list[i].count;
3495 /* ---------- initialize access direction -------------------------------- */
3497 /* initialize access direction values to default (access from every side) */
3498 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3499 if (!IS_CUSTOM_ELEMENT(i))
3500 element_info[i].access_direction = MV_ALL_DIRECTIONS;
3502 /* set access direction value for certain elements from pre-defined list */
3503 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3504 element_info[access_direction_list[i].element].access_direction =
3505 access_direction_list[i].direction;
3507 /* ---------- initialize explosion content ------------------------------- */
3508 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3510 if (IS_CUSTOM_ELEMENT(i))
3513 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3515 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3517 element_info[i].content.e[x][y] =
3518 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3519 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3520 i == EL_PLAYER_3 ? EL_EMERALD :
3521 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3522 i == EL_MOLE ? EL_EMERALD_RED :
3523 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3524 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3525 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3526 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3527 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3528 i == EL_WALL_EMERALD ? EL_EMERALD :
3529 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3530 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3531 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3532 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3533 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3534 i == EL_WALL_PEARL ? EL_PEARL :
3535 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3540 /* ---------- initialize recursion detection ------------------------------ */
3541 recursion_loop_depth = 0;
3542 recursion_loop_detected = FALSE;
3543 recursion_loop_element = EL_UNDEFINED;
3545 /* ---------- initialize graphics engine ---------------------------------- */
3546 game.scroll_delay_value =
3547 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3548 setup.scroll_delay ? setup.scroll_delay_value : 0);
3549 game.scroll_delay_value =
3550 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3553 int get_num_special_action(int element, int action_first, int action_last)
3555 int num_special_action = 0;
3558 for (i = action_first; i <= action_last; i++)
3560 boolean found = FALSE;
3562 for (j = 0; j < NUM_DIRECTIONS; j++)
3563 if (el_act_dir2img(element, i, j) !=
3564 el_act_dir2img(element, ACTION_DEFAULT, j))
3568 num_special_action++;
3573 return num_special_action;
3578 =============================================================================
3580 -----------------------------------------------------------------------------
3581 initialize and start new game
3582 =============================================================================
3587 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3588 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3589 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3591 boolean do_fading = (game_status == GAME_MODE_MAIN);
3595 game_status = GAME_MODE_PLAYING;
3598 InitGameControlValues();
3600 /* don't play tapes over network */
3601 network_playing = (options.network && !tape.playing);
3603 for (i = 0; i < MAX_PLAYERS; i++)
3605 struct PlayerInfo *player = &stored_player[i];
3607 player->index_nr = i;
3608 player->index_bit = (1 << i);
3609 player->element_nr = EL_PLAYER_1 + i;
3611 player->present = FALSE;
3612 player->active = FALSE;
3613 player->killed = FALSE;
3616 player->effective_action = 0;
3617 player->programmed_action = 0;
3620 player->score_final = 0;
3622 player->gems_still_needed = level.gems_needed;
3623 player->sokobanfields_still_needed = 0;
3624 player->lights_still_needed = 0;
3625 player->friends_still_needed = 0;
3627 for (j = 0; j < MAX_NUM_KEYS; j++)
3628 player->key[j] = FALSE;
3630 player->num_white_keys = 0;
3632 player->dynabomb_count = 0;
3633 player->dynabomb_size = 1;
3634 player->dynabombs_left = 0;
3635 player->dynabomb_xl = FALSE;
3637 player->MovDir = MV_NONE;
3640 player->GfxDir = MV_NONE;
3641 player->GfxAction = ACTION_DEFAULT;
3643 player->StepFrame = 0;
3645 player->use_murphy = FALSE;
3646 player->artwork_element =
3647 (level.use_artwork_element[i] ? level.artwork_element[i] :
3648 player->element_nr);
3650 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3651 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3653 player->gravity = level.initial_player_gravity[i];
3655 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3657 player->actual_frame_counter = 0;
3659 player->step_counter = 0;
3661 player->last_move_dir = MV_NONE;
3663 player->is_active = FALSE;
3665 player->is_waiting = FALSE;
3666 player->is_moving = FALSE;
3667 player->is_auto_moving = FALSE;
3668 player->is_digging = FALSE;
3669 player->is_snapping = FALSE;
3670 player->is_collecting = FALSE;
3671 player->is_pushing = FALSE;
3672 player->is_switching = FALSE;
3673 player->is_dropping = FALSE;
3674 player->is_dropping_pressed = FALSE;
3676 player->is_bored = FALSE;
3677 player->is_sleeping = FALSE;
3679 player->frame_counter_bored = -1;
3680 player->frame_counter_sleeping = -1;
3682 player->anim_delay_counter = 0;
3683 player->post_delay_counter = 0;
3685 player->dir_waiting = MV_NONE;
3686 player->action_waiting = ACTION_DEFAULT;
3687 player->last_action_waiting = ACTION_DEFAULT;
3688 player->special_action_bored = ACTION_DEFAULT;
3689 player->special_action_sleeping = ACTION_DEFAULT;
3691 player->switch_x = -1;
3692 player->switch_y = -1;
3694 player->drop_x = -1;
3695 player->drop_y = -1;
3697 player->show_envelope = 0;
3699 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3701 player->push_delay = -1; /* initialized when pushing starts */
3702 player->push_delay_value = game.initial_push_delay_value;
3704 player->drop_delay = 0;
3705 player->drop_pressed_delay = 0;
3707 player->last_jx = -1;
3708 player->last_jy = -1;
3712 player->shield_normal_time_left = 0;
3713 player->shield_deadly_time_left = 0;
3715 player->inventory_infinite_element = EL_UNDEFINED;
3716 player->inventory_size = 0;
3718 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3719 SnapField(player, 0, 0);
3721 player->LevelSolved = FALSE;
3722 player->GameOver = FALSE;
3724 player->LevelSolved_GameWon = FALSE;
3725 player->LevelSolved_GameEnd = FALSE;
3726 player->LevelSolved_PanelOff = FALSE;
3727 player->LevelSolved_SaveTape = FALSE;
3728 player->LevelSolved_SaveScore = FALSE;
3729 player->LevelSolved_CountingTime = 0;
3730 player->LevelSolved_CountingScore = 0;
3733 network_player_action_received = FALSE;
3735 #if defined(NETWORK_AVALIABLE)
3736 /* initial null action */
3737 if (network_playing)
3738 SendToServer_MovePlayer(MV_NONE);
3747 TimeLeft = level.time;
3750 ScreenMovDir = MV_NONE;
3754 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3756 AllPlayersGone = FALSE;
3758 game.yamyam_content_nr = 0;
3759 game.robot_wheel_active = FALSE;
3760 game.magic_wall_active = FALSE;
3761 game.magic_wall_time_left = 0;
3762 game.light_time_left = 0;
3763 game.timegate_time_left = 0;
3764 game.switchgate_pos = 0;
3765 game.wind_direction = level.wind_direction_initial;
3767 #if !USE_PLAYER_GRAVITY
3768 game.gravity = FALSE;
3769 game.explosions_delayed = TRUE;
3772 game.lenses_time_left = 0;
3773 game.magnify_time_left = 0;
3775 game.ball_state = level.ball_state_initial;
3776 game.ball_content_nr = 0;
3778 game.envelope_active = FALSE;
3780 /* set focus to local player for network games, else to all players */
3781 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3782 game.centered_player_nr_next = game.centered_player_nr;
3783 game.set_centered_player = FALSE;
3785 if (network_playing && tape.recording)
3787 /* store client dependent player focus when recording network games */
3788 tape.centered_player_nr_next = game.centered_player_nr_next;
3789 tape.set_centered_player = TRUE;
3792 for (i = 0; i < NUM_BELTS; i++)
3794 game.belt_dir[i] = MV_NONE;
3795 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3798 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3799 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3801 SCAN_PLAYFIELD(x, y)
3803 Feld[x][y] = level.field[x][y];
3804 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3805 ChangeDelay[x][y] = 0;
3806 ChangePage[x][y] = -1;
3807 #if USE_NEW_CUSTOM_VALUE
3808 CustomValue[x][y] = 0; /* initialized in InitField() */
3810 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3812 WasJustMoving[x][y] = 0;
3813 WasJustFalling[x][y] = 0;
3814 CheckCollision[x][y] = 0;
3815 CheckImpact[x][y] = 0;
3817 Pushed[x][y] = FALSE;
3819 ChangeCount[x][y] = 0;
3820 ChangeEvent[x][y] = -1;
3822 ExplodePhase[x][y] = 0;
3823 ExplodeDelay[x][y] = 0;
3824 ExplodeField[x][y] = EX_TYPE_NONE;
3826 RunnerVisit[x][y] = 0;
3827 PlayerVisit[x][y] = 0;
3830 GfxRandom[x][y] = INIT_GFX_RANDOM();
3831 GfxElement[x][y] = EL_UNDEFINED;
3832 GfxAction[x][y] = ACTION_DEFAULT;
3833 GfxDir[x][y] = MV_NONE;
3836 SCAN_PLAYFIELD(x, y)
3838 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3840 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3842 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3845 InitField(x, y, TRUE);
3847 ResetGfxAnimation(x, y);
3852 for (i = 0; i < MAX_PLAYERS; i++)
3854 struct PlayerInfo *player = &stored_player[i];
3856 /* set number of special actions for bored and sleeping animation */
3857 player->num_special_action_bored =
3858 get_num_special_action(player->artwork_element,
3859 ACTION_BORING_1, ACTION_BORING_LAST);
3860 player->num_special_action_sleeping =
3861 get_num_special_action(player->artwork_element,
3862 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3865 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3866 emulate_sb ? EMU_SOKOBAN :
3867 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3869 #if USE_NEW_ALL_SLIPPERY
3870 /* initialize type of slippery elements */
3871 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3873 if (!IS_CUSTOM_ELEMENT(i))
3875 /* default: elements slip down either to the left or right randomly */
3876 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3878 /* SP style elements prefer to slip down on the left side */
3879 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3880 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3882 /* BD style elements prefer to slip down on the left side */
3883 if (game.emulation == EMU_BOULDERDASH)
3884 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3889 /* initialize explosion and ignition delay */
3890 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3892 if (!IS_CUSTOM_ELEMENT(i))
3895 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3896 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3897 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3898 int last_phase = (num_phase + 1) * delay;
3899 int half_phase = (num_phase / 2) * delay;
3901 element_info[i].explosion_delay = last_phase - 1;
3902 element_info[i].ignition_delay = half_phase;
3904 if (i == EL_BLACK_ORB)
3905 element_info[i].ignition_delay = 1;
3909 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
3910 element_info[i].explosion_delay = 1;
3912 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
3913 element_info[i].ignition_delay = 1;
3917 /* correct non-moving belts to start moving left */
3918 for (i = 0; i < NUM_BELTS; i++)
3919 if (game.belt_dir[i] == MV_NONE)
3920 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3922 /* check if any connected player was not found in playfield */
3923 for (i = 0; i < MAX_PLAYERS; i++)
3925 struct PlayerInfo *player = &stored_player[i];
3927 if (player->connected && !player->present)
3929 for (j = 0; j < MAX_PLAYERS; j++)
3931 struct PlayerInfo *some_player = &stored_player[j];
3932 int jx = some_player->jx, jy = some_player->jy;
3934 /* assign first free player found that is present in the playfield */
3935 if (some_player->present && !some_player->connected)
3937 player->present = TRUE;
3938 player->active = TRUE;
3940 some_player->present = FALSE;
3941 some_player->active = FALSE;
3943 player->artwork_element = some_player->artwork_element;
3945 player->block_last_field = some_player->block_last_field;
3946 player->block_delay_adjustment = some_player->block_delay_adjustment;
3948 StorePlayer[jx][jy] = player->element_nr;
3949 player->jx = player->last_jx = jx;
3950 player->jy = player->last_jy = jy;
3960 /* when playing a tape, eliminate all players who do not participate */
3962 for (i = 0; i < MAX_PLAYERS; i++)
3964 if (stored_player[i].active && !tape.player_participates[i])
3966 struct PlayerInfo *player = &stored_player[i];
3967 int jx = player->jx, jy = player->jy;
3969 player->active = FALSE;
3970 StorePlayer[jx][jy] = 0;
3971 Feld[jx][jy] = EL_EMPTY;
3975 else if (!options.network && !setup.team_mode) /* && !tape.playing */
3977 /* when in single player mode, eliminate all but the first active player */
3979 for (i = 0; i < MAX_PLAYERS; i++)
3981 if (stored_player[i].active)
3983 for (j = i + 1; j < MAX_PLAYERS; j++)
3985 if (stored_player[j].active)
3987 struct PlayerInfo *player = &stored_player[j];
3988 int jx = player->jx, jy = player->jy;
3990 player->active = FALSE;
3991 player->present = FALSE;
3993 StorePlayer[jx][jy] = 0;
3994 Feld[jx][jy] = EL_EMPTY;
4001 /* when recording the game, store which players take part in the game */
4004 for (i = 0; i < MAX_PLAYERS; i++)
4005 if (stored_player[i].active)
4006 tape.player_participates[i] = TRUE;
4011 for (i = 0; i < MAX_PLAYERS; i++)
4013 struct PlayerInfo *player = &stored_player[i];
4015 printf("Player %d: present == %d, connected == %d, active == %d.\n",
4020 if (local_player == player)
4021 printf("Player %d is local player.\n", i+1);
4025 if (BorderElement == EL_EMPTY)
4028 SBX_Right = lev_fieldx - SCR_FIELDX;
4030 SBY_Lower = lev_fieldy - SCR_FIELDY;
4035 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4037 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4040 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4041 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4043 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4044 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4046 /* if local player not found, look for custom element that might create
4047 the player (make some assumptions about the right custom element) */
4048 if (!local_player->present)
4050 int start_x = 0, start_y = 0;
4051 int found_rating = 0;
4052 int found_element = EL_UNDEFINED;
4053 int player_nr = local_player->index_nr;
4055 SCAN_PLAYFIELD(x, y)
4057 int element = Feld[x][y];
4062 if (level.use_start_element[player_nr] &&
4063 level.start_element[player_nr] == element &&
4070 found_element = element;
4073 if (!IS_CUSTOM_ELEMENT(element))
4076 if (CAN_CHANGE(element))
4078 for (i = 0; i < element_info[element].num_change_pages; i++)
4080 /* check for player created from custom element as single target */
4081 content = element_info[element].change_page[i].target_element;
4082 is_player = ELEM_IS_PLAYER(content);
4084 if (is_player && (found_rating < 3 ||
4085 (found_rating == 3 && element < found_element)))
4091 found_element = element;
4096 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4098 /* check for player created from custom element as explosion content */
4099 content = element_info[element].content.e[xx][yy];
4100 is_player = ELEM_IS_PLAYER(content);
4102 if (is_player && (found_rating < 2 ||
4103 (found_rating == 2 && element < found_element)))
4105 start_x = x + xx - 1;
4106 start_y = y + yy - 1;
4109 found_element = element;
4112 if (!CAN_CHANGE(element))
4115 for (i = 0; i < element_info[element].num_change_pages; i++)
4117 /* check for player created from custom element as extended target */
4119 element_info[element].change_page[i].target_content.e[xx][yy];
4121 is_player = ELEM_IS_PLAYER(content);
4123 if (is_player && (found_rating < 1 ||
4124 (found_rating == 1 && element < found_element)))
4126 start_x = x + xx - 1;
4127 start_y = y + yy - 1;
4130 found_element = element;
4136 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
4137 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4140 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4141 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4146 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
4147 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4148 local_player->jx - MIDPOSX);
4150 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4151 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4152 local_player->jy - MIDPOSY);
4156 /* do not use PLAYING mask for fading out from main screen */
4157 game_status = GAME_MODE_MAIN;
4162 if (!game.restart_level)
4163 CloseDoor(DOOR_CLOSE_1);
4166 if (level_editor_test_game)
4167 FadeSkipNextFadeIn();
4169 FadeSetEnterScreen();
4171 if (level_editor_test_game)
4172 fading = fading_none;
4174 fading = menu.destination;
4178 FadeOut(REDRAW_FIELD);
4181 FadeOut(REDRAW_FIELD);
4185 game_status = GAME_MODE_PLAYING;
4188 /* !!! FIX THIS (START) !!! */
4189 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4191 InitGameEngine_EM();
4193 /* blit playfield from scroll buffer to normal back buffer for fading in */
4194 BlitScreenToBitmap_EM(backbuffer);
4201 /* after drawing the level, correct some elements */
4202 if (game.timegate_time_left == 0)
4203 CloseAllOpenTimegates();
4205 /* blit playfield from scroll buffer to normal back buffer for fading in */
4206 if (setup.soft_scrolling)
4207 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4209 redraw_mask |= REDRAW_FROM_BACKBUFFER;
4211 /* !!! FIX THIS (END) !!! */
4214 FadeIn(REDRAW_FIELD);
4217 FadeIn(REDRAW_FIELD);
4222 if (!game.restart_level)
4224 /* copy default game door content to main double buffer */
4225 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4226 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4229 SetPanelBackground();
4230 SetDrawBackgroundMask(REDRAW_DOOR_1);
4233 UpdateAndDisplayGameControlValues();
4235 UpdateGameDoorValues();
4236 DrawGameDoorValues();
4239 if (!game.restart_level)
4243 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4244 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4245 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4249 /* copy actual game door content to door double buffer for OpenDoor() */
4250 BlitBitmap(drawto, bitmap_db_door,
4251 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4253 OpenDoor(DOOR_OPEN_ALL);
4255 PlaySound(SND_GAME_STARTING);
4257 if (setup.sound_music)
4260 KeyboardAutoRepeatOffUnlessAutoplay();
4264 for (i = 0; i < MAX_PLAYERS; i++)
4265 printf("Player %d %sactive.\n",
4266 i + 1, (stored_player[i].active ? "" : "not "));
4277 game.restart_level = FALSE;
4280 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4282 /* this is used for non-R'n'D game engines to update certain engine values */
4284 /* needed to determine if sounds are played within the visible screen area */
4285 scroll_x = actual_scroll_x;
4286 scroll_y = actual_scroll_y;
4289 void InitMovDir(int x, int y)
4291 int i, element = Feld[x][y];
4292 static int xy[4][2] =
4299 static int direction[3][4] =
4301 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4302 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4303 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4312 Feld[x][y] = EL_BUG;
4313 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4316 case EL_SPACESHIP_RIGHT:
4317 case EL_SPACESHIP_UP:
4318 case EL_SPACESHIP_LEFT:
4319 case EL_SPACESHIP_DOWN:
4320 Feld[x][y] = EL_SPACESHIP;
4321 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4324 case EL_BD_BUTTERFLY_RIGHT:
4325 case EL_BD_BUTTERFLY_UP:
4326 case EL_BD_BUTTERFLY_LEFT:
4327 case EL_BD_BUTTERFLY_DOWN:
4328 Feld[x][y] = EL_BD_BUTTERFLY;
4329 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4332 case EL_BD_FIREFLY_RIGHT:
4333 case EL_BD_FIREFLY_UP:
4334 case EL_BD_FIREFLY_LEFT:
4335 case EL_BD_FIREFLY_DOWN:
4336 Feld[x][y] = EL_BD_FIREFLY;
4337 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4340 case EL_PACMAN_RIGHT:
4342 case EL_PACMAN_LEFT:
4343 case EL_PACMAN_DOWN:
4344 Feld[x][y] = EL_PACMAN;
4345 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4348 case EL_YAMYAM_LEFT:
4349 case EL_YAMYAM_RIGHT:
4351 case EL_YAMYAM_DOWN:
4352 Feld[x][y] = EL_YAMYAM;
4353 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4356 case EL_SP_SNIKSNAK:
4357 MovDir[x][y] = MV_UP;
4360 case EL_SP_ELECTRON:
4361 MovDir[x][y] = MV_LEFT;
4368 Feld[x][y] = EL_MOLE;
4369 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4373 if (IS_CUSTOM_ELEMENT(element))
4375 struct ElementInfo *ei = &element_info[element];
4376 int move_direction_initial = ei->move_direction_initial;
4377 int move_pattern = ei->move_pattern;
4379 if (move_direction_initial == MV_START_PREVIOUS)
4381 if (MovDir[x][y] != MV_NONE)
4384 move_direction_initial = MV_START_AUTOMATIC;
4387 if (move_direction_initial == MV_START_RANDOM)
4388 MovDir[x][y] = 1 << RND(4);
4389 else if (move_direction_initial & MV_ANY_DIRECTION)
4390 MovDir[x][y] = move_direction_initial;
4391 else if (move_pattern == MV_ALL_DIRECTIONS ||
4392 move_pattern == MV_TURNING_LEFT ||
4393 move_pattern == MV_TURNING_RIGHT ||
4394 move_pattern == MV_TURNING_LEFT_RIGHT ||
4395 move_pattern == MV_TURNING_RIGHT_LEFT ||
4396 move_pattern == MV_TURNING_RANDOM)
4397 MovDir[x][y] = 1 << RND(4);
4398 else if (move_pattern == MV_HORIZONTAL)
4399 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4400 else if (move_pattern == MV_VERTICAL)
4401 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4402 else if (move_pattern & MV_ANY_DIRECTION)
4403 MovDir[x][y] = element_info[element].move_pattern;
4404 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4405 move_pattern == MV_ALONG_RIGHT_SIDE)
4407 /* use random direction as default start direction */
4408 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4409 MovDir[x][y] = 1 << RND(4);
4411 for (i = 0; i < NUM_DIRECTIONS; i++)
4413 int x1 = x + xy[i][0];
4414 int y1 = y + xy[i][1];
4416 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4418 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4419 MovDir[x][y] = direction[0][i];
4421 MovDir[x][y] = direction[1][i];
4430 MovDir[x][y] = 1 << RND(4);
4432 if (element != EL_BUG &&
4433 element != EL_SPACESHIP &&
4434 element != EL_BD_BUTTERFLY &&
4435 element != EL_BD_FIREFLY)
4438 for (i = 0; i < NUM_DIRECTIONS; i++)
4440 int x1 = x + xy[i][0];
4441 int y1 = y + xy[i][1];
4443 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4445 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4447 MovDir[x][y] = direction[0][i];
4450 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4451 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4453 MovDir[x][y] = direction[1][i];
4462 GfxDir[x][y] = MovDir[x][y];
4465 void InitAmoebaNr(int x, int y)
4468 int group_nr = AmoebeNachbarNr(x, y);
4472 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4474 if (AmoebaCnt[i] == 0)
4482 AmoebaNr[x][y] = group_nr;
4483 AmoebaCnt[group_nr]++;
4484 AmoebaCnt2[group_nr]++;
4487 static void PlayerWins(struct PlayerInfo *player)
4489 player->LevelSolved = TRUE;
4490 player->GameOver = TRUE;
4492 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4493 level.native_em_level->lev->score : player->score);
4495 player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4496 player->LevelSolved_CountingScore = player->score_final;
4501 static int time, time_final;
4502 static int score, score_final;
4503 static int game_over_delay_1 = 0;
4504 static int game_over_delay_2 = 0;
4505 int game_over_delay_value_1 = 50;
4506 int game_over_delay_value_2 = 50;
4508 if (!local_player->LevelSolved_GameWon)
4512 /* do not start end game actions before the player stops moving (to exit) */
4513 if (local_player->MovPos)
4516 local_player->LevelSolved_GameWon = TRUE;
4517 local_player->LevelSolved_SaveTape = tape.recording;
4518 local_player->LevelSolved_SaveScore = !tape.playing;
4520 if (tape.auto_play) /* tape might already be stopped here */
4521 tape.auto_play_level_solved = TRUE;
4527 game_over_delay_1 = game_over_delay_value_1;
4528 game_over_delay_2 = game_over_delay_value_2;
4530 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4531 score = score_final = local_player->score_final;
4536 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4538 else if (level.time == 0 && TimePlayed < 999)
4541 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4544 local_player->score_final = score_final;
4546 if (level_editor_test_game)
4549 score = score_final;
4552 local_player->LevelSolved_CountingTime = time;
4553 local_player->LevelSolved_CountingScore = score;
4555 game_panel_controls[GAME_PANEL_TIME].value = time;
4556 game_panel_controls[GAME_PANEL_SCORE].value = score;
4558 DisplayGameControlValues();
4560 DrawGameValue_Time(time);
4561 DrawGameValue_Score(score);
4565 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4567 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4569 /* close exit door after last player */
4570 if ((AllPlayersGone &&
4571 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4572 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4573 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4574 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4575 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4577 int element = Feld[ExitX][ExitY];
4580 if (element == EL_EM_EXIT_OPEN ||
4581 element == EL_EM_STEEL_EXIT_OPEN)
4588 Feld[ExitX][ExitY] =
4589 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4590 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4591 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4592 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4593 EL_EM_STEEL_EXIT_CLOSING);
4595 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4599 /* player disappears */
4600 DrawLevelField(ExitX, ExitY);
4603 for (i = 0; i < MAX_PLAYERS; i++)
4605 struct PlayerInfo *player = &stored_player[i];
4607 if (player->present)
4609 RemovePlayer(player);
4611 /* player disappears */
4612 DrawLevelField(player->jx, player->jy);
4617 PlaySound(SND_GAME_WINNING);
4620 if (game_over_delay_1 > 0)
4622 game_over_delay_1--;
4627 if (time != time_final)
4629 int time_to_go = ABS(time_final - time);
4630 int time_count_dir = (time < time_final ? +1 : -1);
4631 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4633 time += time_count_steps * time_count_dir;
4634 score += time_count_steps * level.score[SC_TIME_BONUS];
4637 local_player->LevelSolved_CountingTime = time;
4638 local_player->LevelSolved_CountingScore = score;
4640 game_panel_controls[GAME_PANEL_TIME].value = time;
4641 game_panel_controls[GAME_PANEL_SCORE].value = score;
4643 DisplayGameControlValues();
4645 DrawGameValue_Time(time);
4646 DrawGameValue_Score(score);
4649 if (time == time_final)
4650 StopSound(SND_GAME_LEVELTIME_BONUS);
4651 else if (setup.sound_loops)
4652 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4654 PlaySound(SND_GAME_LEVELTIME_BONUS);
4659 local_player->LevelSolved_PanelOff = TRUE;
4661 if (game_over_delay_2 > 0)
4663 game_over_delay_2--;
4676 boolean raise_level = FALSE;
4678 local_player->LevelSolved_GameEnd = TRUE;
4680 CloseDoor(DOOR_CLOSE_1);
4682 if (local_player->LevelSolved_SaveTape)
4689 SaveTapeChecked(tape.level_nr); /* ask to save tape */
4691 SaveTape(tape.level_nr); /* ask to save tape */
4695 if (level_editor_test_game)
4697 game_status = GAME_MODE_MAIN;
4700 DrawAndFadeInMainMenu(REDRAW_FIELD);
4708 if (!local_player->LevelSolved_SaveScore)
4711 FadeOut(REDRAW_FIELD);
4714 game_status = GAME_MODE_MAIN;
4716 DrawAndFadeInMainMenu(REDRAW_FIELD);
4721 if (level_nr == leveldir_current->handicap_level)
4723 leveldir_current->handicap_level++;
4724 SaveLevelSetup_SeriesInfo();
4727 if (level_nr < leveldir_current->last_level)
4728 raise_level = TRUE; /* advance to next level */
4730 if ((hi_pos = NewHiScore()) >= 0)
4732 game_status = GAME_MODE_SCORES;
4734 DrawHallOfFame(hi_pos);
4745 FadeOut(REDRAW_FIELD);
4748 game_status = GAME_MODE_MAIN;
4756 DrawAndFadeInMainMenu(REDRAW_FIELD);
4765 LoadScore(level_nr);
4767 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4768 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4771 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4773 if (local_player->score_final > highscore[k].Score)
4775 /* player has made it to the hall of fame */
4777 if (k < MAX_SCORE_ENTRIES - 1)
4779 int m = MAX_SCORE_ENTRIES - 1;
4782 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4783 if (strEqual(setup.player_name, highscore[l].Name))
4785 if (m == k) /* player's new highscore overwrites his old one */
4789 for (l = m; l > k; l--)
4791 strcpy(highscore[l].Name, highscore[l - 1].Name);
4792 highscore[l].Score = highscore[l - 1].Score;
4799 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4800 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4801 highscore[k].Score = local_player->score_final;
4807 else if (!strncmp(setup.player_name, highscore[k].Name,
4808 MAX_PLAYER_NAME_LEN))
4809 break; /* player already there with a higher score */
4815 SaveScore(level_nr);
4820 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4822 int element = Feld[x][y];
4823 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4824 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4825 int horiz_move = (dx != 0);
4826 int sign = (horiz_move ? dx : dy);
4827 int step = sign * element_info[element].move_stepsize;
4829 /* special values for move stepsize for spring and things on conveyor belt */
4832 if (CAN_FALL(element) &&
4833 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4834 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4835 else if (element == EL_SPRING)
4836 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4842 inline static int getElementMoveStepsize(int x, int y)
4844 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4847 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4849 if (player->GfxAction != action || player->GfxDir != dir)
4852 printf("Player frame reset! (%d => %d, %d => %d)\n",
4853 player->GfxAction, action, player->GfxDir, dir);
4856 player->GfxAction = action;
4857 player->GfxDir = dir;
4859 player->StepFrame = 0;
4863 #if USE_GFX_RESET_GFX_ANIMATION
4864 static void ResetGfxFrame(int x, int y, boolean redraw)
4866 int element = Feld[x][y];
4867 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4868 int last_gfx_frame = GfxFrame[x][y];
4870 if (graphic_info[graphic].anim_global_sync)
4871 GfxFrame[x][y] = FrameCounter;
4872 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4873 GfxFrame[x][y] = CustomValue[x][y];
4874 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4875 GfxFrame[x][y] = element_info[element].collect_score;
4876 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4877 GfxFrame[x][y] = ChangeDelay[x][y];
4879 if (redraw && GfxFrame[x][y] != last_gfx_frame)
4880 DrawLevelGraphicAnimation(x, y, graphic);
4884 static void ResetGfxAnimation(int x, int y)
4886 GfxAction[x][y] = ACTION_DEFAULT;
4887 GfxDir[x][y] = MovDir[x][y];
4890 #if USE_GFX_RESET_GFX_ANIMATION
4891 ResetGfxFrame(x, y, FALSE);
4895 static void ResetRandomAnimationValue(int x, int y)
4897 GfxRandom[x][y] = INIT_GFX_RANDOM();
4900 void InitMovingField(int x, int y, int direction)
4902 int element = Feld[x][y];
4903 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4904 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4907 boolean is_moving_before, is_moving_after;
4909 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
4912 /* check if element was/is moving or being moved before/after mode change */
4915 is_moving_before = (WasJustMoving[x][y] != 0);
4917 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
4918 is_moving_before = WasJustMoving[x][y];
4921 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
4923 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
4925 /* reset animation only for moving elements which change direction of moving
4926 or which just started or stopped moving
4927 (else CEs with property "can move" / "not moving" are reset each frame) */
4928 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4930 if (is_moving_before != is_moving_after ||
4931 direction != MovDir[x][y])
4932 ResetGfxAnimation(x, y);
4934 if ((is_moving_before || is_moving_after) && !continues_moving)
4935 ResetGfxAnimation(x, y);
4938 if (!continues_moving)
4939 ResetGfxAnimation(x, y);
4942 MovDir[x][y] = direction;
4943 GfxDir[x][y] = direction;
4945 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4946 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4947 direction == MV_DOWN && CAN_FALL(element) ?
4948 ACTION_FALLING : ACTION_MOVING);
4950 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
4951 ACTION_FALLING : ACTION_MOVING);
4954 /* this is needed for CEs with property "can move" / "not moving" */
4956 if (is_moving_after)
4958 if (Feld[newx][newy] == EL_EMPTY)
4959 Feld[newx][newy] = EL_BLOCKED;
4961 MovDir[newx][newy] = MovDir[x][y];
4963 #if USE_NEW_CUSTOM_VALUE
4964 CustomValue[newx][newy] = CustomValue[x][y];
4967 GfxFrame[newx][newy] = GfxFrame[x][y];
4968 GfxRandom[newx][newy] = GfxRandom[x][y];
4969 GfxAction[newx][newy] = GfxAction[x][y];
4970 GfxDir[newx][newy] = GfxDir[x][y];
4974 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4976 int direction = MovDir[x][y];
4977 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4978 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
4984 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4986 int oldx = x, oldy = y;
4987 int direction = MovDir[x][y];
4989 if (direction == MV_LEFT)
4991 else if (direction == MV_RIGHT)
4993 else if (direction == MV_UP)
4995 else if (direction == MV_DOWN)
4998 *comes_from_x = oldx;
4999 *comes_from_y = oldy;
5002 int MovingOrBlocked2Element(int x, int y)
5004 int element = Feld[x][y];
5006 if (element == EL_BLOCKED)
5010 Blocked2Moving(x, y, &oldx, &oldy);
5011 return Feld[oldx][oldy];
5017 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5019 /* like MovingOrBlocked2Element(), but if element is moving
5020 and (x,y) is the field the moving element is just leaving,
5021 return EL_BLOCKED instead of the element value */
5022 int element = Feld[x][y];
5024 if (IS_MOVING(x, y))
5026 if (element == EL_BLOCKED)
5030 Blocked2Moving(x, y, &oldx, &oldy);
5031 return Feld[oldx][oldy];
5040 static void RemoveField(int x, int y)
5042 Feld[x][y] = EL_EMPTY;
5048 #if USE_NEW_CUSTOM_VALUE
5049 CustomValue[x][y] = 0;
5053 ChangeDelay[x][y] = 0;
5054 ChangePage[x][y] = -1;
5055 Pushed[x][y] = FALSE;
5058 ExplodeField[x][y] = EX_TYPE_NONE;
5061 GfxElement[x][y] = EL_UNDEFINED;
5062 GfxAction[x][y] = ACTION_DEFAULT;
5063 GfxDir[x][y] = MV_NONE;
5066 void RemoveMovingField(int x, int y)
5068 int oldx = x, oldy = y, newx = x, newy = y;
5069 int element = Feld[x][y];
5070 int next_element = EL_UNDEFINED;
5072 if (element != EL_BLOCKED && !IS_MOVING(x, y))
5075 if (IS_MOVING(x, y))
5077 Moving2Blocked(x, y, &newx, &newy);
5079 if (Feld[newx][newy] != EL_BLOCKED)
5081 /* element is moving, but target field is not free (blocked), but
5082 already occupied by something different (example: acid pool);
5083 in this case, only remove the moving field, but not the target */
5085 RemoveField(oldx, oldy);
5087 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5089 DrawLevelField(oldx, oldy);
5094 else if (element == EL_BLOCKED)
5096 Blocked2Moving(x, y, &oldx, &oldy);
5097 if (!IS_MOVING(oldx, oldy))
5101 if (element == EL_BLOCKED &&
5102 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5103 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5104 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5105 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5106 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5107 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5108 next_element = get_next_element(Feld[oldx][oldy]);
5110 RemoveField(oldx, oldy);
5111 RemoveField(newx, newy);
5113 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5115 if (next_element != EL_UNDEFINED)
5116 Feld[oldx][oldy] = next_element;
5118 DrawLevelField(oldx, oldy);
5119 DrawLevelField(newx, newy);
5122 void DrawDynamite(int x, int y)
5124 int sx = SCREENX(x), sy = SCREENY(y);
5125 int graphic = el2img(Feld[x][y]);
5128 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5131 if (IS_WALKABLE_INSIDE(Back[x][y]))
5135 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5136 else if (Store[x][y])
5137 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5139 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5141 if (Back[x][y] || Store[x][y])
5142 DrawGraphicThruMask(sx, sy, graphic, frame);
5144 DrawGraphic(sx, sy, graphic, frame);
5147 void CheckDynamite(int x, int y)
5149 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5153 if (MovDelay[x][y] != 0)
5156 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5162 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5167 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5169 boolean num_checked_players = 0;
5172 for (i = 0; i < MAX_PLAYERS; i++)
5174 if (stored_player[i].active)
5176 int sx = stored_player[i].jx;
5177 int sy = stored_player[i].jy;
5179 if (num_checked_players == 0)
5186 *sx1 = MIN(*sx1, sx);
5187 *sy1 = MIN(*sy1, sy);
5188 *sx2 = MAX(*sx2, sx);
5189 *sy2 = MAX(*sy2, sy);
5192 num_checked_players++;
5197 static boolean checkIfAllPlayersFitToScreen_RND()
5199 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5201 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5203 return (sx2 - sx1 < SCR_FIELDX &&
5204 sy2 - sy1 < SCR_FIELDY);
5207 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5209 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5211 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5213 *sx = (sx1 + sx2) / 2;
5214 *sy = (sy1 + sy2) / 2;
5217 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5218 boolean center_screen, boolean quick_relocation)
5220 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5221 boolean no_delay = (tape.warp_forward);
5222 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5223 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5225 if (quick_relocation)
5227 int offset = game.scroll_delay_value;
5229 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5231 if (!level.shifted_relocation || center_screen)
5233 /* quick relocation (without scrolling), with centering of screen */
5235 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5236 x > SBX_Right + MIDPOSX ? SBX_Right :
5239 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5240 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5245 /* quick relocation (without scrolling), but do not center screen */
5247 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5248 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5251 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5252 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5255 int offset_x = x + (scroll_x - center_scroll_x);
5256 int offset_y = y + (scroll_y - center_scroll_y);
5258 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5259 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5260 offset_x - MIDPOSX);
5262 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5263 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5264 offset_y - MIDPOSY);
5269 /* quick relocation (without scrolling), inside visible screen area */
5271 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
5272 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5273 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5275 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
5276 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5277 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5279 /* don't scroll over playfield boundaries */
5280 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5281 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5283 /* don't scroll over playfield boundaries */
5284 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5285 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5288 RedrawPlayfield(TRUE, 0,0,0,0);
5293 int scroll_xx, scroll_yy;
5295 if (!level.shifted_relocation || center_screen)
5297 /* visible relocation (with scrolling), with centering of screen */
5299 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5300 x > SBX_Right + MIDPOSX ? SBX_Right :
5303 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5304 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5309 /* visible relocation (with scrolling), but do not center screen */
5311 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5312 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5315 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5316 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5319 int offset_x = x + (scroll_x - center_scroll_x);
5320 int offset_y = y + (scroll_y - center_scroll_y);
5322 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5323 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5324 offset_x - MIDPOSX);
5326 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5327 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5328 offset_y - MIDPOSY);
5333 /* visible relocation (with scrolling), with centering of screen */
5335 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5336 x > SBX_Right + MIDPOSX ? SBX_Right :
5339 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5340 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5344 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5346 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5349 int fx = FX, fy = FY;
5351 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5352 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5354 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5360 fx += dx * TILEX / 2;
5361 fy += dy * TILEY / 2;
5363 ScrollLevel(dx, dy);
5366 /* scroll in two steps of half tile size to make things smoother */
5367 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5369 Delay(wait_delay_value);
5371 /* scroll second step to align at full tile size */
5373 Delay(wait_delay_value);
5378 Delay(wait_delay_value);
5382 void RelocatePlayer(int jx, int jy, int el_player_raw)
5384 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5385 int player_nr = GET_PLAYER_NR(el_player);
5386 struct PlayerInfo *player = &stored_player[player_nr];
5387 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5388 boolean no_delay = (tape.warp_forward);
5389 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5390 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5391 int old_jx = player->jx;
5392 int old_jy = player->jy;
5393 int old_element = Feld[old_jx][old_jy];
5394 int element = Feld[jx][jy];
5395 boolean player_relocated = (old_jx != jx || old_jy != jy);
5397 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5398 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5399 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5400 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5401 int leave_side_horiz = move_dir_horiz;
5402 int leave_side_vert = move_dir_vert;
5403 int enter_side = enter_side_horiz | enter_side_vert;
5404 int leave_side = leave_side_horiz | leave_side_vert;
5406 if (player->GameOver) /* do not reanimate dead player */
5409 if (!player_relocated) /* no need to relocate the player */
5412 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5414 RemoveField(jx, jy); /* temporarily remove newly placed player */
5415 DrawLevelField(jx, jy);
5418 if (player->present)
5420 while (player->MovPos)
5422 ScrollPlayer(player, SCROLL_GO_ON);
5423 ScrollScreen(NULL, SCROLL_GO_ON);
5425 AdvanceFrameAndPlayerCounters(player->index_nr);
5430 Delay(wait_delay_value);
5433 DrawPlayer(player); /* needed here only to cleanup last field */
5434 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5436 player->is_moving = FALSE;
5439 if (IS_CUSTOM_ELEMENT(old_element))
5440 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5442 player->index_bit, leave_side);
5444 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5446 player->index_bit, leave_side);
5448 Feld[jx][jy] = el_player;
5449 InitPlayerField(jx, jy, el_player, TRUE);
5451 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5453 Feld[jx][jy] = element;
5454 InitField(jx, jy, FALSE);
5457 /* only visually relocate centered player */
5458 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5459 FALSE, level.instant_relocation);
5461 TestIfPlayerTouchesBadThing(jx, jy);
5462 TestIfPlayerTouchesCustomElement(jx, jy);
5464 if (IS_CUSTOM_ELEMENT(element))
5465 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5466 player->index_bit, enter_side);
5468 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5469 player->index_bit, enter_side);
5472 void Explode(int ex, int ey, int phase, int mode)
5478 /* !!! eliminate this variable !!! */
5479 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5481 if (game.explosions_delayed)
5483 ExplodeField[ex][ey] = mode;
5487 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5489 int center_element = Feld[ex][ey];
5490 int artwork_element, explosion_element; /* set these values later */
5493 /* --- This is only really needed (and now handled) in "Impact()". --- */
5494 /* do not explode moving elements that left the explode field in time */
5495 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5496 center_element == EL_EMPTY &&
5497 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5502 /* !!! at this place, the center element may be EL_BLOCKED !!! */
5503 if (mode == EX_TYPE_NORMAL ||
5504 mode == EX_TYPE_CENTER ||
5505 mode == EX_TYPE_CROSS)
5506 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5509 /* remove things displayed in background while burning dynamite */
5510 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5513 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5515 /* put moving element to center field (and let it explode there) */
5516 center_element = MovingOrBlocked2Element(ex, ey);
5517 RemoveMovingField(ex, ey);
5518 Feld[ex][ey] = center_element;
5521 /* now "center_element" is finally determined -- set related values now */
5522 artwork_element = center_element; /* for custom player artwork */
5523 explosion_element = center_element; /* for custom player artwork */
5525 if (IS_PLAYER(ex, ey))
5527 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5529 artwork_element = stored_player[player_nr].artwork_element;
5531 if (level.use_explosion_element[player_nr])
5533 explosion_element = level.explosion_element[player_nr];
5534 artwork_element = explosion_element;
5539 if (mode == EX_TYPE_NORMAL ||
5540 mode == EX_TYPE_CENTER ||
5541 mode == EX_TYPE_CROSS)
5542 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5545 last_phase = element_info[explosion_element].explosion_delay + 1;
5547 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5549 int xx = x - ex + 1;
5550 int yy = y - ey + 1;
5553 if (!IN_LEV_FIELD(x, y) ||
5554 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5555 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5558 element = Feld[x][y];
5560 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5562 element = MovingOrBlocked2Element(x, y);
5564 if (!IS_EXPLOSION_PROOF(element))
5565 RemoveMovingField(x, y);
5568 /* indestructible elements can only explode in center (but not flames) */
5569 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5570 mode == EX_TYPE_BORDER)) ||
5571 element == EL_FLAMES)
5574 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5575 behaviour, for example when touching a yamyam that explodes to rocks
5576 with active deadly shield, a rock is created under the player !!! */
5577 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5579 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5580 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5581 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5583 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5586 if (IS_ACTIVE_BOMB(element))
5588 /* re-activate things under the bomb like gate or penguin */
5589 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5596 /* save walkable background elements while explosion on same tile */
5597 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5598 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5599 Back[x][y] = element;
5601 /* ignite explodable elements reached by other explosion */
5602 if (element == EL_EXPLOSION)
5603 element = Store2[x][y];
5605 if (AmoebaNr[x][y] &&
5606 (element == EL_AMOEBA_FULL ||
5607 element == EL_BD_AMOEBA ||
5608 element == EL_AMOEBA_GROWING))
5610 AmoebaCnt[AmoebaNr[x][y]]--;
5611 AmoebaCnt2[AmoebaNr[x][y]]--;
5616 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5618 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5620 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5622 if (PLAYERINFO(ex, ey)->use_murphy)
5623 Store[x][y] = EL_EMPTY;
5626 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5627 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5628 else if (ELEM_IS_PLAYER(center_element))
5629 Store[x][y] = EL_EMPTY;
5630 else if (center_element == EL_YAMYAM)
5631 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5632 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5633 Store[x][y] = element_info[center_element].content.e[xx][yy];
5635 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5636 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5637 otherwise) -- FIX THIS !!! */
5638 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5639 Store[x][y] = element_info[element].content.e[1][1];
5641 else if (!CAN_EXPLODE(element))
5642 Store[x][y] = element_info[element].content.e[1][1];
5645 Store[x][y] = EL_EMPTY;
5647 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5648 center_element == EL_AMOEBA_TO_DIAMOND)
5649 Store2[x][y] = element;
5651 Feld[x][y] = EL_EXPLOSION;
5652 GfxElement[x][y] = artwork_element;
5654 ExplodePhase[x][y] = 1;
5655 ExplodeDelay[x][y] = last_phase;
5660 if (center_element == EL_YAMYAM)
5661 game.yamyam_content_nr =
5662 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5674 GfxFrame[x][y] = 0; /* restart explosion animation */
5676 last_phase = ExplodeDelay[x][y];
5678 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5682 /* activate this even in non-DEBUG version until cause for crash in
5683 getGraphicAnimationFrame() (see below) is found and eliminated */
5689 /* this can happen if the player leaves an explosion just in time */
5690 if (GfxElement[x][y] == EL_UNDEFINED)
5691 GfxElement[x][y] = EL_EMPTY;
5693 if (GfxElement[x][y] == EL_UNDEFINED)
5696 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5697 printf("Explode(): This should never happen!\n");
5700 GfxElement[x][y] = EL_EMPTY;
5706 border_element = Store2[x][y];
5707 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5708 border_element = StorePlayer[x][y];
5710 if (phase == element_info[border_element].ignition_delay ||
5711 phase == last_phase)
5713 boolean border_explosion = FALSE;
5715 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5716 !PLAYER_EXPLOSION_PROTECTED(x, y))
5718 KillPlayerUnlessExplosionProtected(x, y);
5719 border_explosion = TRUE;
5721 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5723 Feld[x][y] = Store2[x][y];
5726 border_explosion = TRUE;
5728 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5730 AmoebeUmwandeln(x, y);
5732 border_explosion = TRUE;
5735 /* if an element just explodes due to another explosion (chain-reaction),
5736 do not immediately end the new explosion when it was the last frame of
5737 the explosion (as it would be done in the following "if"-statement!) */
5738 if (border_explosion && phase == last_phase)
5742 if (phase == last_phase)
5746 element = Feld[x][y] = Store[x][y];
5747 Store[x][y] = Store2[x][y] = 0;
5748 GfxElement[x][y] = EL_UNDEFINED;
5750 /* player can escape from explosions and might therefore be still alive */
5751 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5752 element <= EL_PLAYER_IS_EXPLODING_4)
5754 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5755 int explosion_element = EL_PLAYER_1 + player_nr;
5756 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5757 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5759 if (level.use_explosion_element[player_nr])
5760 explosion_element = level.explosion_element[player_nr];
5762 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5763 element_info[explosion_element].content.e[xx][yy]);
5766 /* restore probably existing indestructible background element */
5767 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5768 element = Feld[x][y] = Back[x][y];
5771 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5772 GfxDir[x][y] = MV_NONE;
5773 ChangeDelay[x][y] = 0;
5774 ChangePage[x][y] = -1;
5776 #if USE_NEW_CUSTOM_VALUE
5777 CustomValue[x][y] = 0;
5780 InitField_WithBug2(x, y, FALSE);
5782 DrawLevelField(x, y);
5784 TestIfElementTouchesCustomElement(x, y);
5786 if (GFX_CRUMBLED(element))
5787 DrawLevelFieldCrumbledSandNeighbours(x, y);
5789 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5790 StorePlayer[x][y] = 0;
5792 if (ELEM_IS_PLAYER(element))
5793 RelocatePlayer(x, y, element);
5795 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5797 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5798 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5801 DrawLevelFieldCrumbledSand(x, y);
5803 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5805 DrawLevelElement(x, y, Back[x][y]);
5806 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5808 else if (IS_WALKABLE_UNDER(Back[x][y]))
5810 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5811 DrawLevelElementThruMask(x, y, Back[x][y]);
5813 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5814 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5818 void DynaExplode(int ex, int ey)
5821 int dynabomb_element = Feld[ex][ey];
5822 int dynabomb_size = 1;
5823 boolean dynabomb_xl = FALSE;
5824 struct PlayerInfo *player;
5825 static int xy[4][2] =
5833 if (IS_ACTIVE_BOMB(dynabomb_element))
5835 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5836 dynabomb_size = player->dynabomb_size;
5837 dynabomb_xl = player->dynabomb_xl;
5838 player->dynabombs_left++;
5841 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5843 for (i = 0; i < NUM_DIRECTIONS; i++)
5845 for (j = 1; j <= dynabomb_size; j++)
5847 int x = ex + j * xy[i][0];
5848 int y = ey + j * xy[i][1];
5851 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5854 element = Feld[x][y];
5856 /* do not restart explosions of fields with active bombs */
5857 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5860 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5862 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5863 !IS_DIGGABLE(element) && !dynabomb_xl)
5869 void Bang(int x, int y)
5871 int element = MovingOrBlocked2Element(x, y);
5872 int explosion_type = EX_TYPE_NORMAL;
5874 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5876 struct PlayerInfo *player = PLAYERINFO(x, y);
5878 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
5879 player->element_nr);
5881 if (level.use_explosion_element[player->index_nr])
5883 int explosion_element = level.explosion_element[player->index_nr];
5885 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5886 explosion_type = EX_TYPE_CROSS;
5887 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5888 explosion_type = EX_TYPE_CENTER;
5896 case EL_BD_BUTTERFLY:
5899 case EL_DARK_YAMYAM:
5903 RaiseScoreElement(element);
5906 case EL_DYNABOMB_PLAYER_1_ACTIVE:
5907 case EL_DYNABOMB_PLAYER_2_ACTIVE:
5908 case EL_DYNABOMB_PLAYER_3_ACTIVE:
5909 case EL_DYNABOMB_PLAYER_4_ACTIVE:
5910 case EL_DYNABOMB_INCREASE_NUMBER:
5911 case EL_DYNABOMB_INCREASE_SIZE:
5912 case EL_DYNABOMB_INCREASE_POWER:
5913 explosion_type = EX_TYPE_DYNA;
5916 case EL_DC_LANDMINE:
5918 case EL_EM_EXIT_OPEN:
5919 case EL_EM_STEEL_EXIT_OPEN:
5921 explosion_type = EX_TYPE_CENTER;
5926 case EL_LAMP_ACTIVE:
5927 case EL_AMOEBA_TO_DIAMOND:
5928 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
5929 explosion_type = EX_TYPE_CENTER;
5933 if (element_info[element].explosion_type == EXPLODES_CROSS)
5934 explosion_type = EX_TYPE_CROSS;
5935 else if (element_info[element].explosion_type == EXPLODES_1X1)
5936 explosion_type = EX_TYPE_CENTER;
5940 if (explosion_type == EX_TYPE_DYNA)
5943 Explode(x, y, EX_PHASE_START, explosion_type);
5945 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5948 void SplashAcid(int x, int y)
5950 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5951 (!IN_LEV_FIELD(x - 1, y - 2) ||
5952 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5953 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5955 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5956 (!IN_LEV_FIELD(x + 1, y - 2) ||
5957 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5958 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5960 PlayLevelSound(x, y, SND_ACID_SPLASHING);
5963 static void InitBeltMovement()
5965 static int belt_base_element[4] =
5967 EL_CONVEYOR_BELT_1_LEFT,
5968 EL_CONVEYOR_BELT_2_LEFT,
5969 EL_CONVEYOR_BELT_3_LEFT,
5970 EL_CONVEYOR_BELT_4_LEFT
5972 static int belt_base_active_element[4] =
5974 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5975 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5976 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5977 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5982 /* set frame order for belt animation graphic according to belt direction */
5983 for (i = 0; i < NUM_BELTS; i++)
5987 for (j = 0; j < NUM_BELT_PARTS; j++)
5989 int element = belt_base_active_element[belt_nr] + j;
5990 int graphic_1 = el2img(element);
5991 int graphic_2 = el2panelimg(element);
5993 if (game.belt_dir[i] == MV_LEFT)
5995 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5996 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6000 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6001 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6006 SCAN_PLAYFIELD(x, y)
6008 int element = Feld[x][y];
6010 for (i = 0; i < NUM_BELTS; i++)
6012 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6014 int e_belt_nr = getBeltNrFromBeltElement(element);
6017 if (e_belt_nr == belt_nr)
6019 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6021 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6028 static void ToggleBeltSwitch(int x, int y)
6030 static int belt_base_element[4] =
6032 EL_CONVEYOR_BELT_1_LEFT,
6033 EL_CONVEYOR_BELT_2_LEFT,
6034 EL_CONVEYOR_BELT_3_LEFT,
6035 EL_CONVEYOR_BELT_4_LEFT
6037 static int belt_base_active_element[4] =
6039 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6040 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6041 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6042 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6044 static int belt_base_switch_element[4] =
6046 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6047 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6048 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6049 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6051 static int belt_move_dir[4] =
6059 int element = Feld[x][y];
6060 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6061 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6062 int belt_dir = belt_move_dir[belt_dir_nr];
6065 if (!IS_BELT_SWITCH(element))
6068 game.belt_dir_nr[belt_nr] = belt_dir_nr;
6069 game.belt_dir[belt_nr] = belt_dir;
6071 if (belt_dir_nr == 3)
6074 /* set frame order for belt animation graphic according to belt direction */
6075 for (i = 0; i < NUM_BELT_PARTS; i++)
6077 int element = belt_base_active_element[belt_nr] + i;
6078 int graphic_1 = el2img(element);
6079 int graphic_2 = el2panelimg(element);
6081 if (belt_dir == MV_LEFT)
6083 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6084 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6088 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6089 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6093 SCAN_PLAYFIELD(xx, yy)
6095 int element = Feld[xx][yy];
6097 if (IS_BELT_SWITCH(element))
6099 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6101 if (e_belt_nr == belt_nr)
6103 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6104 DrawLevelField(xx, yy);
6107 else if (IS_BELT(element) && belt_dir != MV_NONE)
6109 int e_belt_nr = getBeltNrFromBeltElement(element);
6111 if (e_belt_nr == belt_nr)
6113 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6115 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6116 DrawLevelField(xx, yy);
6119 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6121 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6123 if (e_belt_nr == belt_nr)
6125 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6127 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6128 DrawLevelField(xx, yy);
6134 static void ToggleSwitchgateSwitch(int x, int y)
6138 game.switchgate_pos = !game.switchgate_pos;
6140 SCAN_PLAYFIELD(xx, yy)
6142 int element = Feld[xx][yy];
6144 #if !USE_BOTH_SWITCHGATE_SWITCHES
6145 if (element == EL_SWITCHGATE_SWITCH_UP ||
6146 element == EL_SWITCHGATE_SWITCH_DOWN)
6148 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6149 DrawLevelField(xx, yy);
6151 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6152 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6154 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6155 DrawLevelField(xx, yy);
6158 if (element == EL_SWITCHGATE_SWITCH_UP)
6160 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6161 DrawLevelField(xx, yy);
6163 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6165 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6166 DrawLevelField(xx, yy);
6168 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6170 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6171 DrawLevelField(xx, yy);
6173 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6175 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6176 DrawLevelField(xx, yy);
6179 else if (element == EL_SWITCHGATE_OPEN ||
6180 element == EL_SWITCHGATE_OPENING)
6182 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6184 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6186 else if (element == EL_SWITCHGATE_CLOSED ||
6187 element == EL_SWITCHGATE_CLOSING)
6189 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6191 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6196 static int getInvisibleActiveFromInvisibleElement(int element)
6198 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6199 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6200 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6204 static int getInvisibleFromInvisibleActiveElement(int element)
6206 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6207 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6208 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6212 static void RedrawAllLightSwitchesAndInvisibleElements()
6216 SCAN_PLAYFIELD(x, y)
6218 int element = Feld[x][y];
6220 if (element == EL_LIGHT_SWITCH &&
6221 game.light_time_left > 0)
6223 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6224 DrawLevelField(x, y);
6226 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6227 game.light_time_left == 0)
6229 Feld[x][y] = EL_LIGHT_SWITCH;
6230 DrawLevelField(x, y);
6232 else if (element == EL_EMC_DRIPPER &&
6233 game.light_time_left > 0)
6235 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6236 DrawLevelField(x, y);
6238 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6239 game.light_time_left == 0)
6241 Feld[x][y] = EL_EMC_DRIPPER;
6242 DrawLevelField(x, y);
6244 else if (element == EL_INVISIBLE_STEELWALL ||
6245 element == EL_INVISIBLE_WALL ||
6246 element == EL_INVISIBLE_SAND)
6248 if (game.light_time_left > 0)
6249 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6251 DrawLevelField(x, y);
6253 /* uncrumble neighbour fields, if needed */
6254 if (element == EL_INVISIBLE_SAND)
6255 DrawLevelFieldCrumbledSandNeighbours(x, y);
6257 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6258 element == EL_INVISIBLE_WALL_ACTIVE ||
6259 element == EL_INVISIBLE_SAND_ACTIVE)
6261 if (game.light_time_left == 0)
6262 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6264 DrawLevelField(x, y);
6266 /* re-crumble neighbour fields, if needed */
6267 if (element == EL_INVISIBLE_SAND)
6268 DrawLevelFieldCrumbledSandNeighbours(x, y);
6273 static void RedrawAllInvisibleElementsForLenses()
6277 SCAN_PLAYFIELD(x, y)
6279 int element = Feld[x][y];
6281 if (element == EL_EMC_DRIPPER &&
6282 game.lenses_time_left > 0)
6284 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6285 DrawLevelField(x, y);
6287 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6288 game.lenses_time_left == 0)
6290 Feld[x][y] = EL_EMC_DRIPPER;
6291 DrawLevelField(x, y);
6293 else if (element == EL_INVISIBLE_STEELWALL ||
6294 element == EL_INVISIBLE_WALL ||
6295 element == EL_INVISIBLE_SAND)
6297 if (game.lenses_time_left > 0)
6298 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6300 DrawLevelField(x, y);
6302 /* uncrumble neighbour fields, if needed */
6303 if (element == EL_INVISIBLE_SAND)
6304 DrawLevelFieldCrumbledSandNeighbours(x, y);
6306 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6307 element == EL_INVISIBLE_WALL_ACTIVE ||
6308 element == EL_INVISIBLE_SAND_ACTIVE)
6310 if (game.lenses_time_left == 0)
6311 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6313 DrawLevelField(x, y);
6315 /* re-crumble neighbour fields, if needed */
6316 if (element == EL_INVISIBLE_SAND)
6317 DrawLevelFieldCrumbledSandNeighbours(x, y);
6322 static void RedrawAllInvisibleElementsForMagnifier()
6326 SCAN_PLAYFIELD(x, y)
6328 int element = Feld[x][y];
6330 if (element == EL_EMC_FAKE_GRASS &&
6331 game.magnify_time_left > 0)
6333 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6334 DrawLevelField(x, y);
6336 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6337 game.magnify_time_left == 0)
6339 Feld[x][y] = EL_EMC_FAKE_GRASS;
6340 DrawLevelField(x, y);
6342 else if (IS_GATE_GRAY(element) &&
6343 game.magnify_time_left > 0)
6345 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6346 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6347 IS_EM_GATE_GRAY(element) ?
6348 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6349 IS_EMC_GATE_GRAY(element) ?
6350 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6352 DrawLevelField(x, y);
6354 else if (IS_GATE_GRAY_ACTIVE(element) &&
6355 game.magnify_time_left == 0)
6357 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6358 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6359 IS_EM_GATE_GRAY_ACTIVE(element) ?
6360 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6361 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6362 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6364 DrawLevelField(x, y);
6369 static void ToggleLightSwitch(int x, int y)
6371 int element = Feld[x][y];
6373 game.light_time_left =
6374 (element == EL_LIGHT_SWITCH ?
6375 level.time_light * FRAMES_PER_SECOND : 0);
6377 RedrawAllLightSwitchesAndInvisibleElements();
6380 static void ActivateTimegateSwitch(int x, int y)
6384 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6386 SCAN_PLAYFIELD(xx, yy)
6388 int element = Feld[xx][yy];
6390 if (element == EL_TIMEGATE_CLOSED ||
6391 element == EL_TIMEGATE_CLOSING)
6393 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6394 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6398 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6400 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6401 DrawLevelField(xx, yy);
6408 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6409 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6411 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6415 void Impact(int x, int y)
6417 boolean last_line = (y == lev_fieldy - 1);
6418 boolean object_hit = FALSE;
6419 boolean impact = (last_line || object_hit);
6420 int element = Feld[x][y];
6421 int smashed = EL_STEELWALL;
6423 if (!last_line) /* check if element below was hit */
6425 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6428 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6429 MovDir[x][y + 1] != MV_DOWN ||
6430 MovPos[x][y + 1] <= TILEY / 2));
6432 /* do not smash moving elements that left the smashed field in time */
6433 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6434 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6437 #if USE_QUICKSAND_IMPACT_BUGFIX
6438 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6440 RemoveMovingField(x, y + 1);
6441 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6442 Feld[x][y + 2] = EL_ROCK;
6443 DrawLevelField(x, y + 2);
6448 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6450 RemoveMovingField(x, y + 1);
6451 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6452 Feld[x][y + 2] = EL_ROCK;
6453 DrawLevelField(x, y + 2);
6460 smashed = MovingOrBlocked2Element(x, y + 1);
6462 impact = (last_line || object_hit);
6465 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6467 SplashAcid(x, y + 1);
6471 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6472 /* only reset graphic animation if graphic really changes after impact */
6474 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6476 ResetGfxAnimation(x, y);
6477 DrawLevelField(x, y);
6480 if (impact && CAN_EXPLODE_IMPACT(element))
6485 else if (impact && element == EL_PEARL &&
6486 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6488 ResetGfxAnimation(x, y);
6490 Feld[x][y] = EL_PEARL_BREAKING;
6491 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6494 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6496 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6501 if (impact && element == EL_AMOEBA_DROP)
6503 if (object_hit && IS_PLAYER(x, y + 1))
6504 KillPlayerUnlessEnemyProtected(x, y + 1);
6505 else if (object_hit && smashed == EL_PENGUIN)
6509 Feld[x][y] = EL_AMOEBA_GROWING;
6510 Store[x][y] = EL_AMOEBA_WET;
6512 ResetRandomAnimationValue(x, y);
6517 if (object_hit) /* check which object was hit */
6519 if ((CAN_PASS_MAGIC_WALL(element) &&
6520 (smashed == EL_MAGIC_WALL ||
6521 smashed == EL_BD_MAGIC_WALL)) ||
6522 (CAN_PASS_DC_MAGIC_WALL(element) &&
6523 smashed == EL_DC_MAGIC_WALL))
6526 int activated_magic_wall =
6527 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6528 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6529 EL_DC_MAGIC_WALL_ACTIVE);
6531 /* activate magic wall / mill */
6532 SCAN_PLAYFIELD(xx, yy)
6534 if (Feld[xx][yy] == smashed)
6535 Feld[xx][yy] = activated_magic_wall;
6538 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6539 game.magic_wall_active = TRUE;
6541 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6542 SND_MAGIC_WALL_ACTIVATING :
6543 smashed == EL_BD_MAGIC_WALL ?
6544 SND_BD_MAGIC_WALL_ACTIVATING :
6545 SND_DC_MAGIC_WALL_ACTIVATING));
6548 if (IS_PLAYER(x, y + 1))
6550 if (CAN_SMASH_PLAYER(element))
6552 KillPlayerUnlessEnemyProtected(x, y + 1);
6556 else if (smashed == EL_PENGUIN)
6558 if (CAN_SMASH_PLAYER(element))
6564 else if (element == EL_BD_DIAMOND)
6566 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6572 else if (((element == EL_SP_INFOTRON ||
6573 element == EL_SP_ZONK) &&
6574 (smashed == EL_SP_SNIKSNAK ||
6575 smashed == EL_SP_ELECTRON ||
6576 smashed == EL_SP_DISK_ORANGE)) ||
6577 (element == EL_SP_INFOTRON &&
6578 smashed == EL_SP_DISK_YELLOW))
6583 else if (CAN_SMASH_EVERYTHING(element))
6585 if (IS_CLASSIC_ENEMY(smashed) ||
6586 CAN_EXPLODE_SMASHED(smashed))
6591 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6593 if (smashed == EL_LAMP ||
6594 smashed == EL_LAMP_ACTIVE)
6599 else if (smashed == EL_NUT)
6601 Feld[x][y + 1] = EL_NUT_BREAKING;
6602 PlayLevelSound(x, y, SND_NUT_BREAKING);
6603 RaiseScoreElement(EL_NUT);
6606 else if (smashed == EL_PEARL)
6608 ResetGfxAnimation(x, y);
6610 Feld[x][y + 1] = EL_PEARL_BREAKING;
6611 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6614 else if (smashed == EL_DIAMOND)
6616 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6617 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6620 else if (IS_BELT_SWITCH(smashed))
6622 ToggleBeltSwitch(x, y + 1);
6624 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6625 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6626 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6627 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6629 ToggleSwitchgateSwitch(x, y + 1);
6631 else if (smashed == EL_LIGHT_SWITCH ||
6632 smashed == EL_LIGHT_SWITCH_ACTIVE)
6634 ToggleLightSwitch(x, y + 1);
6639 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6642 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6644 CheckElementChangeBySide(x, y + 1, smashed, element,
6645 CE_SWITCHED, CH_SIDE_TOP);
6646 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6652 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6657 /* play sound of magic wall / mill */
6659 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6660 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6661 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6663 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6664 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6665 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6666 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6667 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6668 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6673 /* play sound of object that hits the ground */
6674 if (last_line || object_hit)
6675 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6678 inline static void TurnRoundExt(int x, int y)
6690 { 0, 0 }, { 0, 0 }, { 0, 0 },
6695 int left, right, back;
6699 { MV_DOWN, MV_UP, MV_RIGHT },
6700 { MV_UP, MV_DOWN, MV_LEFT },
6702 { MV_LEFT, MV_RIGHT, MV_DOWN },
6706 { MV_RIGHT, MV_LEFT, MV_UP }
6709 int element = Feld[x][y];
6710 int move_pattern = element_info[element].move_pattern;
6712 int old_move_dir = MovDir[x][y];
6713 int left_dir = turn[old_move_dir].left;
6714 int right_dir = turn[old_move_dir].right;
6715 int back_dir = turn[old_move_dir].back;
6717 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6718 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6719 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6720 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6722 int left_x = x + left_dx, left_y = y + left_dy;
6723 int right_x = x + right_dx, right_y = y + right_dy;
6724 int move_x = x + move_dx, move_y = y + move_dy;
6728 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6730 TestIfBadThingTouchesOtherBadThing(x, y);
6732 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6733 MovDir[x][y] = right_dir;
6734 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6735 MovDir[x][y] = left_dir;
6737 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6739 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6742 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6744 TestIfBadThingTouchesOtherBadThing(x, y);
6746 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6747 MovDir[x][y] = left_dir;
6748 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6749 MovDir[x][y] = right_dir;
6751 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6753 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6756 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6758 TestIfBadThingTouchesOtherBadThing(x, y);
6760 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6761 MovDir[x][y] = left_dir;
6762 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6763 MovDir[x][y] = right_dir;
6765 if (MovDir[x][y] != old_move_dir)
6768 else if (element == EL_YAMYAM)
6770 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6771 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6773 if (can_turn_left && can_turn_right)
6774 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6775 else if (can_turn_left)
6776 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6777 else if (can_turn_right)
6778 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6780 MovDir[x][y] = back_dir;
6782 MovDelay[x][y] = 16 + 16 * RND(3);
6784 else if (element == EL_DARK_YAMYAM)
6786 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6788 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6791 if (can_turn_left && can_turn_right)
6792 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6793 else if (can_turn_left)
6794 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6795 else if (can_turn_right)
6796 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6798 MovDir[x][y] = back_dir;
6800 MovDelay[x][y] = 16 + 16 * RND(3);
6802 else if (element == EL_PACMAN)
6804 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6805 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6807 if (can_turn_left && can_turn_right)
6808 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6809 else if (can_turn_left)
6810 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6811 else if (can_turn_right)
6812 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6814 MovDir[x][y] = back_dir;
6816 MovDelay[x][y] = 6 + RND(40);
6818 else if (element == EL_PIG)
6820 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6821 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6822 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6823 boolean should_turn_left, should_turn_right, should_move_on;
6825 int rnd = RND(rnd_value);
6827 should_turn_left = (can_turn_left &&
6829 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6830 y + back_dy + left_dy)));
6831 should_turn_right = (can_turn_right &&
6833 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6834 y + back_dy + right_dy)));
6835 should_move_on = (can_move_on &&
6838 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6839 y + move_dy + left_dy) ||
6840 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6841 y + move_dy + right_dy)));
6843 if (should_turn_left || should_turn_right || should_move_on)
6845 if (should_turn_left && should_turn_right && should_move_on)
6846 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6847 rnd < 2 * rnd_value / 3 ? right_dir :
6849 else if (should_turn_left && should_turn_right)
6850 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6851 else if (should_turn_left && should_move_on)
6852 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6853 else if (should_turn_right && should_move_on)
6854 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6855 else if (should_turn_left)
6856 MovDir[x][y] = left_dir;
6857 else if (should_turn_right)
6858 MovDir[x][y] = right_dir;
6859 else if (should_move_on)
6860 MovDir[x][y] = old_move_dir;
6862 else if (can_move_on && rnd > rnd_value / 8)
6863 MovDir[x][y] = old_move_dir;
6864 else if (can_turn_left && can_turn_right)
6865 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6866 else if (can_turn_left && rnd > rnd_value / 8)
6867 MovDir[x][y] = left_dir;
6868 else if (can_turn_right && rnd > rnd_value/8)
6869 MovDir[x][y] = right_dir;
6871 MovDir[x][y] = back_dir;
6873 xx = x + move_xy[MovDir[x][y]].dx;
6874 yy = y + move_xy[MovDir[x][y]].dy;
6876 if (!IN_LEV_FIELD(xx, yy) ||
6877 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6878 MovDir[x][y] = old_move_dir;
6882 else if (element == EL_DRAGON)
6884 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6885 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6886 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6888 int rnd = RND(rnd_value);
6890 if (can_move_on && rnd > rnd_value / 8)
6891 MovDir[x][y] = old_move_dir;
6892 else if (can_turn_left && can_turn_right)
6893 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6894 else if (can_turn_left && rnd > rnd_value / 8)
6895 MovDir[x][y] = left_dir;
6896 else if (can_turn_right && rnd > rnd_value / 8)
6897 MovDir[x][y] = right_dir;
6899 MovDir[x][y] = back_dir;
6901 xx = x + move_xy[MovDir[x][y]].dx;
6902 yy = y + move_xy[MovDir[x][y]].dy;
6904 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6905 MovDir[x][y] = old_move_dir;
6909 else if (element == EL_MOLE)
6911 boolean can_move_on =
6912 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6913 IS_AMOEBOID(Feld[move_x][move_y]) ||
6914 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6917 boolean can_turn_left =
6918 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6919 IS_AMOEBOID(Feld[left_x][left_y])));
6921 boolean can_turn_right =
6922 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6923 IS_AMOEBOID(Feld[right_x][right_y])));
6925 if (can_turn_left && can_turn_right)
6926 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6927 else if (can_turn_left)
6928 MovDir[x][y] = left_dir;
6930 MovDir[x][y] = right_dir;
6933 if (MovDir[x][y] != old_move_dir)
6936 else if (element == EL_BALLOON)
6938 MovDir[x][y] = game.wind_direction;
6941 else if (element == EL_SPRING)
6943 #if USE_NEW_SPRING_BUMPER
6944 if (MovDir[x][y] & MV_HORIZONTAL)
6946 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6947 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6949 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6950 ResetGfxAnimation(move_x, move_y);
6951 DrawLevelField(move_x, move_y);
6953 MovDir[x][y] = back_dir;
6955 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6956 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6957 MovDir[x][y] = MV_NONE;
6960 if (MovDir[x][y] & MV_HORIZONTAL &&
6961 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6962 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
6963 MovDir[x][y] = MV_NONE;
6968 else if (element == EL_ROBOT ||
6969 element == EL_SATELLITE ||
6970 element == EL_PENGUIN ||
6971 element == EL_EMC_ANDROID)
6973 int attr_x = -1, attr_y = -1;
6984 for (i = 0; i < MAX_PLAYERS; i++)
6986 struct PlayerInfo *player = &stored_player[i];
6987 int jx = player->jx, jy = player->jy;
6989 if (!player->active)
6993 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7001 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7002 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7003 game.engine_version < VERSION_IDENT(3,1,0,0)))
7009 if (element == EL_PENGUIN)
7012 static int xy[4][2] =
7020 for (i = 0; i < NUM_DIRECTIONS; i++)
7022 int ex = x + xy[i][0];
7023 int ey = y + xy[i][1];
7025 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7026 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7027 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7028 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7037 MovDir[x][y] = MV_NONE;
7039 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7040 else if (attr_x > x)
7041 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7043 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7044 else if (attr_y > y)
7045 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7047 if (element == EL_ROBOT)
7051 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7052 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7053 Moving2Blocked(x, y, &newx, &newy);
7055 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7056 MovDelay[x][y] = 8 + 8 * !RND(3);
7058 MovDelay[x][y] = 16;
7060 else if (element == EL_PENGUIN)
7066 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7068 boolean first_horiz = RND(2);
7069 int new_move_dir = MovDir[x][y];
7072 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7073 Moving2Blocked(x, y, &newx, &newy);
7075 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7079 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7080 Moving2Blocked(x, y, &newx, &newy);
7082 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7085 MovDir[x][y] = old_move_dir;
7089 else if (element == EL_SATELLITE)
7095 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7097 boolean first_horiz = RND(2);
7098 int new_move_dir = MovDir[x][y];
7101 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7102 Moving2Blocked(x, y, &newx, &newy);
7104 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7108 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7109 Moving2Blocked(x, y, &newx, &newy);
7111 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7114 MovDir[x][y] = old_move_dir;
7118 else if (element == EL_EMC_ANDROID)
7120 static int check_pos[16] =
7122 -1, /* 0 => (invalid) */
7123 7, /* 1 => MV_LEFT */
7124 3, /* 2 => MV_RIGHT */
7125 -1, /* 3 => (invalid) */
7127 0, /* 5 => MV_LEFT | MV_UP */
7128 2, /* 6 => MV_RIGHT | MV_UP */
7129 -1, /* 7 => (invalid) */
7130 5, /* 8 => MV_DOWN */
7131 6, /* 9 => MV_LEFT | MV_DOWN */
7132 4, /* 10 => MV_RIGHT | MV_DOWN */
7133 -1, /* 11 => (invalid) */
7134 -1, /* 12 => (invalid) */
7135 -1, /* 13 => (invalid) */
7136 -1, /* 14 => (invalid) */
7137 -1, /* 15 => (invalid) */
7145 { -1, -1, MV_LEFT | MV_UP },
7147 { +1, -1, MV_RIGHT | MV_UP },
7148 { +1, 0, MV_RIGHT },
7149 { +1, +1, MV_RIGHT | MV_DOWN },
7151 { -1, +1, MV_LEFT | MV_DOWN },
7154 int start_pos, check_order;
7155 boolean can_clone = FALSE;
7158 /* check if there is any free field around current position */
7159 for (i = 0; i < 8; i++)
7161 int newx = x + check_xy[i].dx;
7162 int newy = y + check_xy[i].dy;
7164 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7172 if (can_clone) /* randomly find an element to clone */
7176 start_pos = check_pos[RND(8)];
7177 check_order = (RND(2) ? -1 : +1);
7179 for (i = 0; i < 8; i++)
7181 int pos_raw = start_pos + i * check_order;
7182 int pos = (pos_raw + 8) % 8;
7183 int newx = x + check_xy[pos].dx;
7184 int newy = y + check_xy[pos].dy;
7186 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7188 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7189 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7191 Store[x][y] = Feld[newx][newy];
7200 if (can_clone) /* randomly find a direction to move */
7204 start_pos = check_pos[RND(8)];
7205 check_order = (RND(2) ? -1 : +1);
7207 for (i = 0; i < 8; i++)
7209 int pos_raw = start_pos + i * check_order;
7210 int pos = (pos_raw + 8) % 8;
7211 int newx = x + check_xy[pos].dx;
7212 int newy = y + check_xy[pos].dy;
7213 int new_move_dir = check_xy[pos].dir;
7215 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7217 MovDir[x][y] = new_move_dir;
7218 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7227 if (can_clone) /* cloning and moving successful */
7230 /* cannot clone -- try to move towards player */
7232 start_pos = check_pos[MovDir[x][y] & 0x0f];
7233 check_order = (RND(2) ? -1 : +1);
7235 for (i = 0; i < 3; i++)
7237 /* first check start_pos, then previous/next or (next/previous) pos */
7238 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7239 int pos = (pos_raw + 8) % 8;
7240 int newx = x + check_xy[pos].dx;
7241 int newy = y + check_xy[pos].dy;
7242 int new_move_dir = check_xy[pos].dir;
7244 if (IS_PLAYER(newx, newy))
7247 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7249 MovDir[x][y] = new_move_dir;
7250 MovDelay[x][y] = level.android_move_time * 8 + 1;
7257 else if (move_pattern == MV_TURNING_LEFT ||
7258 move_pattern == MV_TURNING_RIGHT ||
7259 move_pattern == MV_TURNING_LEFT_RIGHT ||
7260 move_pattern == MV_TURNING_RIGHT_LEFT ||
7261 move_pattern == MV_TURNING_RANDOM ||
7262 move_pattern == MV_ALL_DIRECTIONS)
7264 boolean can_turn_left =
7265 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7266 boolean can_turn_right =
7267 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7269 if (element_info[element].move_stepsize == 0) /* "not moving" */
7272 if (move_pattern == MV_TURNING_LEFT)
7273 MovDir[x][y] = left_dir;
7274 else if (move_pattern == MV_TURNING_RIGHT)
7275 MovDir[x][y] = right_dir;
7276 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7277 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7278 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7279 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7280 else if (move_pattern == MV_TURNING_RANDOM)
7281 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7282 can_turn_right && !can_turn_left ? right_dir :
7283 RND(2) ? left_dir : right_dir);
7284 else if (can_turn_left && can_turn_right)
7285 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7286 else if (can_turn_left)
7287 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7288 else if (can_turn_right)
7289 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7291 MovDir[x][y] = back_dir;
7293 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7295 else if (move_pattern == MV_HORIZONTAL ||
7296 move_pattern == MV_VERTICAL)
7298 if (move_pattern & old_move_dir)
7299 MovDir[x][y] = back_dir;
7300 else if (move_pattern == MV_HORIZONTAL)
7301 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7302 else if (move_pattern == MV_VERTICAL)
7303 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7305 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7307 else if (move_pattern & MV_ANY_DIRECTION)
7309 MovDir[x][y] = move_pattern;
7310 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7312 else if (move_pattern & MV_WIND_DIRECTION)
7314 MovDir[x][y] = game.wind_direction;
7315 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7317 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7319 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7320 MovDir[x][y] = left_dir;
7321 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7322 MovDir[x][y] = right_dir;
7324 if (MovDir[x][y] != old_move_dir)
7325 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7327 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7329 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7330 MovDir[x][y] = right_dir;
7331 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7332 MovDir[x][y] = left_dir;
7334 if (MovDir[x][y] != old_move_dir)
7335 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7337 else if (move_pattern == MV_TOWARDS_PLAYER ||
7338 move_pattern == MV_AWAY_FROM_PLAYER)
7340 int attr_x = -1, attr_y = -1;
7342 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7353 for (i = 0; i < MAX_PLAYERS; i++)
7355 struct PlayerInfo *player = &stored_player[i];
7356 int jx = player->jx, jy = player->jy;
7358 if (!player->active)
7362 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7370 MovDir[x][y] = MV_NONE;
7372 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7373 else if (attr_x > x)
7374 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7376 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7377 else if (attr_y > y)
7378 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7380 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7382 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7384 boolean first_horiz = RND(2);
7385 int new_move_dir = MovDir[x][y];
7387 if (element_info[element].move_stepsize == 0) /* "not moving" */
7389 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7390 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7396 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7397 Moving2Blocked(x, y, &newx, &newy);
7399 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7403 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7404 Moving2Blocked(x, y, &newx, &newy);
7406 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7409 MovDir[x][y] = old_move_dir;
7412 else if (move_pattern == MV_WHEN_PUSHED ||
7413 move_pattern == MV_WHEN_DROPPED)
7415 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7416 MovDir[x][y] = MV_NONE;
7420 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7422 static int test_xy[7][2] =
7432 static int test_dir[7] =
7442 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7443 int move_preference = -1000000; /* start with very low preference */
7444 int new_move_dir = MV_NONE;
7445 int start_test = RND(4);
7448 for (i = 0; i < NUM_DIRECTIONS; i++)
7450 int move_dir = test_dir[start_test + i];
7451 int move_dir_preference;
7453 xx = x + test_xy[start_test + i][0];
7454 yy = y + test_xy[start_test + i][1];
7456 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7457 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7459 new_move_dir = move_dir;
7464 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7467 move_dir_preference = -1 * RunnerVisit[xx][yy];
7468 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7469 move_dir_preference = PlayerVisit[xx][yy];
7471 if (move_dir_preference > move_preference)
7473 /* prefer field that has not been visited for the longest time */
7474 move_preference = move_dir_preference;
7475 new_move_dir = move_dir;
7477 else if (move_dir_preference == move_preference &&
7478 move_dir == old_move_dir)
7480 /* prefer last direction when all directions are preferred equally */
7481 move_preference = move_dir_preference;
7482 new_move_dir = move_dir;
7486 MovDir[x][y] = new_move_dir;
7487 if (old_move_dir != new_move_dir)
7488 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7492 static void TurnRound(int x, int y)
7494 int direction = MovDir[x][y];
7498 GfxDir[x][y] = MovDir[x][y];
7500 if (direction != MovDir[x][y])
7504 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7506 ResetGfxFrame(x, y, FALSE);
7509 static boolean JustBeingPushed(int x, int y)
7513 for (i = 0; i < MAX_PLAYERS; i++)
7515 struct PlayerInfo *player = &stored_player[i];
7517 if (player->active && player->is_pushing && player->MovPos)
7519 int next_jx = player->jx + (player->jx - player->last_jx);
7520 int next_jy = player->jy + (player->jy - player->last_jy);
7522 if (x == next_jx && y == next_jy)
7530 void StartMoving(int x, int y)
7532 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7533 int element = Feld[x][y];
7538 if (MovDelay[x][y] == 0)
7539 GfxAction[x][y] = ACTION_DEFAULT;
7541 if (CAN_FALL(element) && y < lev_fieldy - 1)
7543 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7544 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7545 if (JustBeingPushed(x, y))
7548 if (element == EL_QUICKSAND_FULL)
7550 if (IS_FREE(x, y + 1))
7552 InitMovingField(x, y, MV_DOWN);
7553 started_moving = TRUE;
7555 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7556 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7557 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7558 Store[x][y] = EL_ROCK;
7560 Store[x][y] = EL_ROCK;
7563 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7565 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7567 if (!MovDelay[x][y])
7568 MovDelay[x][y] = TILEY + 1;
7577 Feld[x][y] = EL_QUICKSAND_EMPTY;
7578 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7579 Store[x][y + 1] = Store[x][y];
7582 PlayLevelSoundAction(x, y, ACTION_FILLING);
7585 else if (element == EL_QUICKSAND_FAST_FULL)
7587 if (IS_FREE(x, y + 1))
7589 InitMovingField(x, y, MV_DOWN);
7590 started_moving = TRUE;
7592 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7593 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7594 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7595 Store[x][y] = EL_ROCK;
7597 Store[x][y] = EL_ROCK;
7600 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7602 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7604 if (!MovDelay[x][y])
7605 MovDelay[x][y] = TILEY + 1;
7614 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7615 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7616 Store[x][y + 1] = Store[x][y];
7619 PlayLevelSoundAction(x, y, ACTION_FILLING);
7622 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7623 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7625 InitMovingField(x, y, MV_DOWN);
7626 started_moving = TRUE;
7628 Feld[x][y] = EL_QUICKSAND_FILLING;
7629 Store[x][y] = element;
7631 PlayLevelSoundAction(x, y, ACTION_FILLING);
7633 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7634 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7636 InitMovingField(x, y, MV_DOWN);
7637 started_moving = TRUE;
7639 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7640 Store[x][y] = element;
7642 PlayLevelSoundAction(x, y, ACTION_FILLING);
7644 else if (element == EL_MAGIC_WALL_FULL)
7646 if (IS_FREE(x, y + 1))
7648 InitMovingField(x, y, MV_DOWN);
7649 started_moving = TRUE;
7651 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7652 Store[x][y] = EL_CHANGED(Store[x][y]);
7654 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7656 if (!MovDelay[x][y])
7657 MovDelay[x][y] = TILEY/4 + 1;
7666 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7667 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7668 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7672 else if (element == EL_BD_MAGIC_WALL_FULL)
7674 if (IS_FREE(x, y + 1))
7676 InitMovingField(x, y, MV_DOWN);
7677 started_moving = TRUE;
7679 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7680 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7682 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7684 if (!MovDelay[x][y])
7685 MovDelay[x][y] = TILEY/4 + 1;
7694 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7695 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7696 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7700 else if (element == EL_DC_MAGIC_WALL_FULL)
7702 if (IS_FREE(x, y + 1))
7704 InitMovingField(x, y, MV_DOWN);
7705 started_moving = TRUE;
7707 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7708 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7710 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7712 if (!MovDelay[x][y])
7713 MovDelay[x][y] = TILEY/4 + 1;
7722 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7723 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7724 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7728 else if ((CAN_PASS_MAGIC_WALL(element) &&
7729 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7730 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7731 (CAN_PASS_DC_MAGIC_WALL(element) &&
7732 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7735 InitMovingField(x, y, MV_DOWN);
7736 started_moving = TRUE;
7739 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7740 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7741 EL_DC_MAGIC_WALL_FILLING);
7742 Store[x][y] = element;
7744 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7746 SplashAcid(x, y + 1);
7748 InitMovingField(x, y, MV_DOWN);
7749 started_moving = TRUE;
7751 Store[x][y] = EL_ACID;
7754 #if USE_FIX_IMPACT_COLLISION
7755 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7756 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7758 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7759 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7761 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7762 CAN_FALL(element) && WasJustFalling[x][y] &&
7763 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7765 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7766 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7767 (Feld[x][y + 1] == EL_BLOCKED)))
7769 /* this is needed for a special case not covered by calling "Impact()"
7770 from "ContinueMoving()": if an element moves to a tile directly below
7771 another element which was just falling on that tile (which was empty
7772 in the previous frame), the falling element above would just stop
7773 instead of smashing the element below (in previous version, the above
7774 element was just checked for "moving" instead of "falling", resulting
7775 in incorrect smashes caused by horizontal movement of the above
7776 element; also, the case of the player being the element to smash was
7777 simply not covered here... :-/ ) */
7779 CheckCollision[x][y] = 0;
7780 CheckImpact[x][y] = 0;
7784 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7786 if (MovDir[x][y] == MV_NONE)
7788 InitMovingField(x, y, MV_DOWN);
7789 started_moving = TRUE;
7792 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7794 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7795 MovDir[x][y] = MV_DOWN;
7797 InitMovingField(x, y, MV_DOWN);
7798 started_moving = TRUE;
7800 else if (element == EL_AMOEBA_DROP)
7802 Feld[x][y] = EL_AMOEBA_GROWING;
7803 Store[x][y] = EL_AMOEBA_WET;
7805 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7806 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7807 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7808 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7810 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
7811 (IS_FREE(x - 1, y + 1) ||
7812 Feld[x - 1][y + 1] == EL_ACID));
7813 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7814 (IS_FREE(x + 1, y + 1) ||
7815 Feld[x + 1][y + 1] == EL_ACID));
7816 boolean can_fall_any = (can_fall_left || can_fall_right);
7817 boolean can_fall_both = (can_fall_left && can_fall_right);
7818 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7820 #if USE_NEW_ALL_SLIPPERY
7821 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7823 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7824 can_fall_right = FALSE;
7825 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7826 can_fall_left = FALSE;
7827 else if (slippery_type == SLIPPERY_ONLY_LEFT)
7828 can_fall_right = FALSE;
7829 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7830 can_fall_left = FALSE;
7832 can_fall_any = (can_fall_left || can_fall_right);
7833 can_fall_both = FALSE;
7836 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
7838 if (slippery_type == SLIPPERY_ONLY_LEFT)
7839 can_fall_right = FALSE;
7840 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7841 can_fall_left = FALSE;
7842 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7843 can_fall_right = FALSE;
7844 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7845 can_fall_left = FALSE;
7847 can_fall_any = (can_fall_left || can_fall_right);
7848 can_fall_both = (can_fall_left && can_fall_right);
7852 #if USE_NEW_ALL_SLIPPERY
7854 #if USE_NEW_SP_SLIPPERY
7855 /* !!! better use the same properties as for custom elements here !!! */
7856 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
7857 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
7859 can_fall_right = FALSE; /* slip down on left side */
7860 can_fall_both = FALSE;
7865 #if USE_NEW_ALL_SLIPPERY
7868 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7869 can_fall_right = FALSE; /* slip down on left side */
7871 can_fall_left = !(can_fall_right = RND(2));
7873 can_fall_both = FALSE;
7878 if (game.emulation == EMU_BOULDERDASH ||
7879 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7880 can_fall_right = FALSE; /* slip down on left side */
7882 can_fall_left = !(can_fall_right = RND(2));
7884 can_fall_both = FALSE;
7890 /* if not determined otherwise, prefer left side for slipping down */
7891 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7892 started_moving = TRUE;
7896 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
7898 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7901 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
7902 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7903 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7904 int belt_dir = game.belt_dir[belt_nr];
7906 if ((belt_dir == MV_LEFT && left_is_free) ||
7907 (belt_dir == MV_RIGHT && right_is_free))
7909 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7911 InitMovingField(x, y, belt_dir);
7912 started_moving = TRUE;
7914 Pushed[x][y] = TRUE;
7915 Pushed[nextx][y] = TRUE;
7917 GfxAction[x][y] = ACTION_DEFAULT;
7921 MovDir[x][y] = 0; /* if element was moving, stop it */
7926 /* not "else if" because of elements that can fall and move (EL_SPRING) */
7928 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
7930 if (CAN_MOVE(element) && !started_moving)
7933 int move_pattern = element_info[element].move_pattern;
7938 if (MovDir[x][y] == MV_NONE)
7940 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
7941 x, y, element, element_info[element].token_name);
7942 printf("StartMoving(): This should never happen!\n");
7947 Moving2Blocked(x, y, &newx, &newy);
7949 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7952 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7953 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7955 WasJustMoving[x][y] = 0;
7956 CheckCollision[x][y] = 0;
7958 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7960 if (Feld[x][y] != element) /* element has changed */
7964 if (!MovDelay[x][y]) /* start new movement phase */
7966 /* all objects that can change their move direction after each step
7967 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7969 if (element != EL_YAMYAM &&
7970 element != EL_DARK_YAMYAM &&
7971 element != EL_PACMAN &&
7972 !(move_pattern & MV_ANY_DIRECTION) &&
7973 move_pattern != MV_TURNING_LEFT &&
7974 move_pattern != MV_TURNING_RIGHT &&
7975 move_pattern != MV_TURNING_LEFT_RIGHT &&
7976 move_pattern != MV_TURNING_RIGHT_LEFT &&
7977 move_pattern != MV_TURNING_RANDOM)
7981 if (MovDelay[x][y] && (element == EL_BUG ||
7982 element == EL_SPACESHIP ||
7983 element == EL_SP_SNIKSNAK ||
7984 element == EL_SP_ELECTRON ||
7985 element == EL_MOLE))
7986 DrawLevelField(x, y);
7990 if (MovDelay[x][y]) /* wait some time before next movement */
7994 if (element == EL_ROBOT ||
7995 element == EL_YAMYAM ||
7996 element == EL_DARK_YAMYAM)
7998 DrawLevelElementAnimationIfNeeded(x, y, element);
7999 PlayLevelSoundAction(x, y, ACTION_WAITING);
8001 else if (element == EL_SP_ELECTRON)
8002 DrawLevelElementAnimationIfNeeded(x, y, element);
8003 else if (element == EL_DRAGON)
8006 int dir = MovDir[x][y];
8007 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8008 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
8009 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
8010 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
8011 dir == MV_UP ? IMG_FLAMES_1_UP :
8012 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8013 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8015 GfxAction[x][y] = ACTION_ATTACKING;
8017 if (IS_PLAYER(x, y))
8018 DrawPlayerField(x, y);
8020 DrawLevelField(x, y);
8022 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8024 for (i = 1; i <= 3; i++)
8026 int xx = x + i * dx;
8027 int yy = y + i * dy;
8028 int sx = SCREENX(xx);
8029 int sy = SCREENY(yy);
8030 int flame_graphic = graphic + (i - 1);
8032 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8037 int flamed = MovingOrBlocked2Element(xx, yy);
8041 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8043 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8044 RemoveMovingField(xx, yy);
8046 RemoveField(xx, yy);
8048 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8051 RemoveMovingField(xx, yy);
8054 ChangeDelay[xx][yy] = 0;
8056 Feld[xx][yy] = EL_FLAMES;
8058 if (IN_SCR_FIELD(sx, sy))
8060 DrawLevelFieldCrumbledSand(xx, yy);
8061 DrawGraphic(sx, sy, flame_graphic, frame);
8066 if (Feld[xx][yy] == EL_FLAMES)
8067 Feld[xx][yy] = EL_EMPTY;
8068 DrawLevelField(xx, yy);
8073 if (MovDelay[x][y]) /* element still has to wait some time */
8075 PlayLevelSoundAction(x, y, ACTION_WAITING);
8081 /* now make next step */
8083 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8085 if (DONT_COLLIDE_WITH(element) &&
8086 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8087 !PLAYER_ENEMY_PROTECTED(newx, newy))
8089 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8094 else if (CAN_MOVE_INTO_ACID(element) &&
8095 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8096 !IS_MV_DIAGONAL(MovDir[x][y]) &&
8097 (MovDir[x][y] == MV_DOWN ||
8098 game.engine_version >= VERSION_IDENT(3,1,0,0)))
8100 SplashAcid(newx, newy);
8101 Store[x][y] = EL_ACID;
8103 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8105 if (Feld[newx][newy] == EL_EXIT_OPEN ||
8106 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8107 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8108 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8111 DrawLevelField(x, y);
8113 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8114 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8115 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8117 local_player->friends_still_needed--;
8118 if (!local_player->friends_still_needed &&
8119 !local_player->GameOver && AllPlayersGone)
8120 PlayerWins(local_player);
8124 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8126 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8127 DrawLevelField(newx, newy);
8129 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8131 else if (!IS_FREE(newx, newy))
8133 GfxAction[x][y] = ACTION_WAITING;
8135 if (IS_PLAYER(x, y))
8136 DrawPlayerField(x, y);
8138 DrawLevelField(x, y);
8143 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8145 if (IS_FOOD_PIG(Feld[newx][newy]))
8147 if (IS_MOVING(newx, newy))
8148 RemoveMovingField(newx, newy);
8151 Feld[newx][newy] = EL_EMPTY;
8152 DrawLevelField(newx, newy);
8155 PlayLevelSound(x, y, SND_PIG_DIGGING);
8157 else if (!IS_FREE(newx, newy))
8159 if (IS_PLAYER(x, y))
8160 DrawPlayerField(x, y);
8162 DrawLevelField(x, y);
8167 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8169 if (Store[x][y] != EL_EMPTY)
8171 boolean can_clone = FALSE;
8174 /* check if element to clone is still there */
8175 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8177 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8185 /* cannot clone or target field not free anymore -- do not clone */
8186 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8187 Store[x][y] = EL_EMPTY;
8190 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8192 if (IS_MV_DIAGONAL(MovDir[x][y]))
8194 int diagonal_move_dir = MovDir[x][y];
8195 int stored = Store[x][y];
8196 int change_delay = 8;
8199 /* android is moving diagonally */
8201 CreateField(x, y, EL_DIAGONAL_SHRINKING);
8203 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8204 GfxElement[x][y] = EL_EMC_ANDROID;
8205 GfxAction[x][y] = ACTION_SHRINKING;
8206 GfxDir[x][y] = diagonal_move_dir;
8207 ChangeDelay[x][y] = change_delay;
8209 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8212 DrawLevelGraphicAnimation(x, y, graphic);
8213 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8215 if (Feld[newx][newy] == EL_ACID)
8217 SplashAcid(newx, newy);
8222 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8224 Store[newx][newy] = EL_EMC_ANDROID;
8225 GfxElement[newx][newy] = EL_EMC_ANDROID;
8226 GfxAction[newx][newy] = ACTION_GROWING;
8227 GfxDir[newx][newy] = diagonal_move_dir;
8228 ChangeDelay[newx][newy] = change_delay;
8230 graphic = el_act_dir2img(GfxElement[newx][newy],
8231 GfxAction[newx][newy], GfxDir[newx][newy]);
8233 DrawLevelGraphicAnimation(newx, newy, graphic);
8234 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8240 Feld[newx][newy] = EL_EMPTY;
8241 DrawLevelField(newx, newy);
8243 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8246 else if (!IS_FREE(newx, newy))
8249 if (IS_PLAYER(x, y))
8250 DrawPlayerField(x, y);
8252 DrawLevelField(x, y);
8258 else if (IS_CUSTOM_ELEMENT(element) &&
8259 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8261 int new_element = Feld[newx][newy];
8263 if (!IS_FREE(newx, newy))
8265 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8266 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8269 /* no element can dig solid indestructible elements */
8270 if (IS_INDESTRUCTIBLE(new_element) &&
8271 !IS_DIGGABLE(new_element) &&
8272 !IS_COLLECTIBLE(new_element))
8275 if (AmoebaNr[newx][newy] &&
8276 (new_element == EL_AMOEBA_FULL ||
8277 new_element == EL_BD_AMOEBA ||
8278 new_element == EL_AMOEBA_GROWING))
8280 AmoebaCnt[AmoebaNr[newx][newy]]--;
8281 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8284 if (IS_MOVING(newx, newy))
8285 RemoveMovingField(newx, newy);
8288 RemoveField(newx, newy);
8289 DrawLevelField(newx, newy);
8292 /* if digged element was about to explode, prevent the explosion */
8293 ExplodeField[newx][newy] = EX_TYPE_NONE;
8295 PlayLevelSoundAction(x, y, action);
8298 Store[newx][newy] = EL_EMPTY;
8300 /* this makes it possible to leave the removed element again */
8301 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8302 Store[newx][newy] = new_element;
8304 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8306 int move_leave_element = element_info[element].move_leave_element;
8308 /* this makes it possible to leave the removed element again */
8309 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8310 new_element : move_leave_element);
8314 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8316 RunnerVisit[x][y] = FrameCounter;
8317 PlayerVisit[x][y] /= 8; /* expire player visit path */
8320 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8322 if (!IS_FREE(newx, newy))
8324 if (IS_PLAYER(x, y))
8325 DrawPlayerField(x, y);
8327 DrawLevelField(x, y);
8333 boolean wanna_flame = !RND(10);
8334 int dx = newx - x, dy = newy - y;
8335 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8336 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8337 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8338 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8339 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8340 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8343 IS_CLASSIC_ENEMY(element1) ||
8344 IS_CLASSIC_ENEMY(element2)) &&
8345 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8346 element1 != EL_FLAMES && element2 != EL_FLAMES)
8348 ResetGfxAnimation(x, y);
8349 GfxAction[x][y] = ACTION_ATTACKING;
8351 if (IS_PLAYER(x, y))
8352 DrawPlayerField(x, y);
8354 DrawLevelField(x, y);
8356 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8358 MovDelay[x][y] = 50;
8362 RemoveField(newx, newy);
8364 Feld[newx][newy] = EL_FLAMES;
8365 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8368 RemoveField(newx1, newy1);
8370 Feld[newx1][newy1] = EL_FLAMES;
8372 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8375 RemoveField(newx2, newy2);
8377 Feld[newx2][newy2] = EL_FLAMES;
8384 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8385 Feld[newx][newy] == EL_DIAMOND)
8387 if (IS_MOVING(newx, newy))
8388 RemoveMovingField(newx, newy);
8391 Feld[newx][newy] = EL_EMPTY;
8392 DrawLevelField(newx, newy);
8395 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8397 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8398 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8400 if (AmoebaNr[newx][newy])
8402 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8403 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8404 Feld[newx][newy] == EL_BD_AMOEBA)
8405 AmoebaCnt[AmoebaNr[newx][newy]]--;
8410 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8412 RemoveMovingField(newx, newy);
8415 if (IS_MOVING(newx, newy))
8417 RemoveMovingField(newx, newy);
8422 Feld[newx][newy] = EL_EMPTY;
8423 DrawLevelField(newx, newy);
8426 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8428 else if ((element == EL_PACMAN || element == EL_MOLE)
8429 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8431 if (AmoebaNr[newx][newy])
8433 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8434 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8435 Feld[newx][newy] == EL_BD_AMOEBA)
8436 AmoebaCnt[AmoebaNr[newx][newy]]--;
8439 if (element == EL_MOLE)
8441 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8442 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8444 ResetGfxAnimation(x, y);
8445 GfxAction[x][y] = ACTION_DIGGING;
8446 DrawLevelField(x, y);
8448 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8450 return; /* wait for shrinking amoeba */
8452 else /* element == EL_PACMAN */
8454 Feld[newx][newy] = EL_EMPTY;
8455 DrawLevelField(newx, newy);
8456 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8459 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8460 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8461 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8463 /* wait for shrinking amoeba to completely disappear */
8466 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8468 /* object was running against a wall */
8473 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8474 if (move_pattern & MV_ANY_DIRECTION &&
8475 move_pattern == MovDir[x][y])
8477 int blocking_element =
8478 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8480 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8483 element = Feld[x][y]; /* element might have changed */
8487 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8488 DrawLevelElementAnimation(x, y, element);
8490 if (DONT_TOUCH(element))
8491 TestIfBadThingTouchesPlayer(x, y);
8496 InitMovingField(x, y, MovDir[x][y]);
8498 PlayLevelSoundAction(x, y, ACTION_MOVING);
8502 ContinueMoving(x, y);
8505 void ContinueMoving(int x, int y)
8507 int element = Feld[x][y];
8508 struct ElementInfo *ei = &element_info[element];
8509 int direction = MovDir[x][y];
8510 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8511 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8512 int newx = x + dx, newy = y + dy;
8513 int stored = Store[x][y];
8514 int stored_new = Store[newx][newy];
8515 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
8516 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8517 boolean last_line = (newy == lev_fieldy - 1);
8519 MovPos[x][y] += getElementMoveStepsize(x, y);
8521 if (pushed_by_player) /* special case: moving object pushed by player */
8522 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8524 if (ABS(MovPos[x][y]) < TILEX)
8527 int ee = Feld[x][y];
8528 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8529 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8531 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8532 x, y, ABS(MovPos[x][y]),
8534 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8537 DrawLevelField(x, y);
8539 return; /* element is still moving */
8542 /* element reached destination field */
8544 Feld[x][y] = EL_EMPTY;
8545 Feld[newx][newy] = element;
8546 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8548 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
8550 element = Feld[newx][newy] = EL_ACID;
8552 else if (element == EL_MOLE)
8554 Feld[x][y] = EL_SAND;
8556 DrawLevelFieldCrumbledSandNeighbours(x, y);
8558 else if (element == EL_QUICKSAND_FILLING)
8560 element = Feld[newx][newy] = get_next_element(element);
8561 Store[newx][newy] = Store[x][y];
8563 else if (element == EL_QUICKSAND_EMPTYING)
8565 Feld[x][y] = get_next_element(element);
8566 element = Feld[newx][newy] = Store[x][y];
8568 else if (element == EL_QUICKSAND_FAST_FILLING)
8570 element = Feld[newx][newy] = get_next_element(element);
8571 Store[newx][newy] = Store[x][y];
8573 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8575 Feld[x][y] = get_next_element(element);
8576 element = Feld[newx][newy] = Store[x][y];
8578 else if (element == EL_MAGIC_WALL_FILLING)
8580 element = Feld[newx][newy] = get_next_element(element);
8581 if (!game.magic_wall_active)
8582 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8583 Store[newx][newy] = Store[x][y];
8585 else if (element == EL_MAGIC_WALL_EMPTYING)
8587 Feld[x][y] = get_next_element(element);
8588 if (!game.magic_wall_active)
8589 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8590 element = Feld[newx][newy] = Store[x][y];
8592 #if USE_NEW_CUSTOM_VALUE
8593 InitField(newx, newy, FALSE);
8596 else if (element == EL_BD_MAGIC_WALL_FILLING)
8598 element = Feld[newx][newy] = get_next_element(element);
8599 if (!game.magic_wall_active)
8600 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8601 Store[newx][newy] = Store[x][y];
8603 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8605 Feld[x][y] = get_next_element(element);
8606 if (!game.magic_wall_active)
8607 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8608 element = Feld[newx][newy] = Store[x][y];
8610 #if USE_NEW_CUSTOM_VALUE
8611 InitField(newx, newy, FALSE);
8614 else if (element == EL_DC_MAGIC_WALL_FILLING)
8616 element = Feld[newx][newy] = get_next_element(element);
8617 if (!game.magic_wall_active)
8618 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8619 Store[newx][newy] = Store[x][y];
8621 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8623 Feld[x][y] = get_next_element(element);
8624 if (!game.magic_wall_active)
8625 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8626 element = Feld[newx][newy] = Store[x][y];
8628 #if USE_NEW_CUSTOM_VALUE
8629 InitField(newx, newy, FALSE);
8632 else if (element == EL_AMOEBA_DROPPING)
8634 Feld[x][y] = get_next_element(element);
8635 element = Feld[newx][newy] = Store[x][y];
8637 else if (element == EL_SOKOBAN_OBJECT)
8640 Feld[x][y] = Back[x][y];
8642 if (Back[newx][newy])
8643 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8645 Back[x][y] = Back[newx][newy] = 0;
8648 Store[x][y] = EL_EMPTY;
8653 MovDelay[newx][newy] = 0;
8655 if (CAN_CHANGE_OR_HAS_ACTION(element))
8657 /* copy element change control values to new field */
8658 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8659 ChangePage[newx][newy] = ChangePage[x][y];
8660 ChangeCount[newx][newy] = ChangeCount[x][y];
8661 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8664 #if USE_NEW_CUSTOM_VALUE
8665 CustomValue[newx][newy] = CustomValue[x][y];
8668 ChangeDelay[x][y] = 0;
8669 ChangePage[x][y] = -1;
8670 ChangeCount[x][y] = 0;
8671 ChangeEvent[x][y] = -1;
8673 #if USE_NEW_CUSTOM_VALUE
8674 CustomValue[x][y] = 0;
8677 /* copy animation control values to new field */
8678 GfxFrame[newx][newy] = GfxFrame[x][y];
8679 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
8680 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
8681 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
8683 Pushed[x][y] = Pushed[newx][newy] = FALSE;
8685 /* some elements can leave other elements behind after moving */
8687 if (ei->move_leave_element != EL_EMPTY &&
8688 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8689 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8691 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
8692 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8693 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8696 int move_leave_element = ei->move_leave_element;
8700 /* this makes it possible to leave the removed element again */
8701 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8702 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8704 /* this makes it possible to leave the removed element again */
8705 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8706 move_leave_element = stored;
8709 /* this makes it possible to leave the removed element again */
8710 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8711 ei->move_leave_element == EL_TRIGGER_ELEMENT)
8712 move_leave_element = stored;
8715 Feld[x][y] = move_leave_element;
8717 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8718 MovDir[x][y] = direction;
8720 InitField(x, y, FALSE);
8722 if (GFX_CRUMBLED(Feld[x][y]))
8723 DrawLevelFieldCrumbledSandNeighbours(x, y);
8725 if (ELEM_IS_PLAYER(move_leave_element))
8726 RelocatePlayer(x, y, move_leave_element);
8729 /* do this after checking for left-behind element */
8730 ResetGfxAnimation(x, y); /* reset animation values for old field */
8732 if (!CAN_MOVE(element) ||
8733 (CAN_FALL(element) && direction == MV_DOWN &&
8734 (element == EL_SPRING ||
8735 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8736 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8737 GfxDir[x][y] = MovDir[newx][newy] = 0;
8739 DrawLevelField(x, y);
8740 DrawLevelField(newx, newy);
8742 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8744 /* prevent pushed element from moving on in pushed direction */
8745 if (pushed_by_player && CAN_MOVE(element) &&
8746 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8747 !(element_info[element].move_pattern & direction))
8748 TurnRound(newx, newy);
8750 /* prevent elements on conveyor belt from moving on in last direction */
8751 if (pushed_by_conveyor && CAN_FALL(element) &&
8752 direction & MV_HORIZONTAL)
8753 MovDir[newx][newy] = 0;
8755 if (!pushed_by_player)
8757 int nextx = newx + dx, nexty = newy + dy;
8758 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8760 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8762 if (CAN_FALL(element) && direction == MV_DOWN)
8763 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8765 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8766 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8768 #if USE_FIX_IMPACT_COLLISION
8769 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8770 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8774 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8776 TestIfBadThingTouchesPlayer(newx, newy);
8777 TestIfBadThingTouchesFriend(newx, newy);
8779 if (!IS_CUSTOM_ELEMENT(element))
8780 TestIfBadThingTouchesOtherBadThing(newx, newy);
8782 else if (element == EL_PENGUIN)
8783 TestIfFriendTouchesBadThing(newx, newy);
8785 /* give the player one last chance (one more frame) to move away */
8786 if (CAN_FALL(element) && direction == MV_DOWN &&
8787 (last_line || (!IS_FREE(x, newy + 1) &&
8788 (!IS_PLAYER(x, newy + 1) ||
8789 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8792 if (pushed_by_player && !game.use_change_when_pushing_bug)
8794 int push_side = MV_DIR_OPPOSITE(direction);
8795 struct PlayerInfo *player = PLAYERINFO(x, y);
8797 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8798 player->index_bit, push_side);
8799 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8800 player->index_bit, push_side);
8803 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
8804 MovDelay[newx][newy] = 1;
8806 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8808 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
8811 if (ChangePage[newx][newy] != -1) /* delayed change */
8813 int page = ChangePage[newx][newy];
8814 struct ElementChangeInfo *change = &ei->change_page[page];
8816 ChangePage[newx][newy] = -1;
8818 if (change->can_change)
8820 if (ChangeElement(newx, newy, element, page))
8822 if (change->post_change_function)
8823 change->post_change_function(newx, newy);
8827 if (change->has_action)
8828 ExecuteCustomElementAction(newx, newy, element, page);
8832 TestIfElementHitsCustomElement(newx, newy, direction);
8833 TestIfPlayerTouchesCustomElement(newx, newy);
8834 TestIfElementTouchesCustomElement(newx, newy);
8836 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8837 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8838 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8839 MV_DIR_OPPOSITE(direction));
8842 int AmoebeNachbarNr(int ax, int ay)
8845 int element = Feld[ax][ay];
8847 static int xy[4][2] =
8855 for (i = 0; i < NUM_DIRECTIONS; i++)
8857 int x = ax + xy[i][0];
8858 int y = ay + xy[i][1];
8860 if (!IN_LEV_FIELD(x, y))
8863 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8864 group_nr = AmoebaNr[x][y];
8870 void AmoebenVereinigen(int ax, int ay)
8872 int i, x, y, xx, yy;
8873 int new_group_nr = AmoebaNr[ax][ay];
8874 static int xy[4][2] =
8882 if (new_group_nr == 0)
8885 for (i = 0; i < NUM_DIRECTIONS; i++)
8890 if (!IN_LEV_FIELD(x, y))
8893 if ((Feld[x][y] == EL_AMOEBA_FULL ||
8894 Feld[x][y] == EL_BD_AMOEBA ||
8895 Feld[x][y] == EL_AMOEBA_DEAD) &&
8896 AmoebaNr[x][y] != new_group_nr)
8898 int old_group_nr = AmoebaNr[x][y];
8900 if (old_group_nr == 0)
8903 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8904 AmoebaCnt[old_group_nr] = 0;
8905 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8906 AmoebaCnt2[old_group_nr] = 0;
8908 SCAN_PLAYFIELD(xx, yy)
8910 if (AmoebaNr[xx][yy] == old_group_nr)
8911 AmoebaNr[xx][yy] = new_group_nr;
8917 void AmoebeUmwandeln(int ax, int ay)
8921 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8923 int group_nr = AmoebaNr[ax][ay];
8928 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8929 printf("AmoebeUmwandeln(): This should never happen!\n");
8934 SCAN_PLAYFIELD(x, y)
8936 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8939 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8943 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8944 SND_AMOEBA_TURNING_TO_GEM :
8945 SND_AMOEBA_TURNING_TO_ROCK));
8950 static int xy[4][2] =
8958 for (i = 0; i < NUM_DIRECTIONS; i++)
8963 if (!IN_LEV_FIELD(x, y))
8966 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8968 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8969 SND_AMOEBA_TURNING_TO_GEM :
8970 SND_AMOEBA_TURNING_TO_ROCK));
8977 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8980 int group_nr = AmoebaNr[ax][ay];
8981 boolean done = FALSE;
8986 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8987 printf("AmoebeUmwandelnBD(): This should never happen!\n");
8992 SCAN_PLAYFIELD(x, y)
8994 if (AmoebaNr[x][y] == group_nr &&
8995 (Feld[x][y] == EL_AMOEBA_DEAD ||
8996 Feld[x][y] == EL_BD_AMOEBA ||
8997 Feld[x][y] == EL_AMOEBA_GROWING))
9000 Feld[x][y] = new_element;
9001 InitField(x, y, FALSE);
9002 DrawLevelField(x, y);
9008 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9009 SND_BD_AMOEBA_TURNING_TO_ROCK :
9010 SND_BD_AMOEBA_TURNING_TO_GEM));
9013 void AmoebeWaechst(int x, int y)
9015 static unsigned long sound_delay = 0;
9016 static unsigned long sound_delay_value = 0;
9018 if (!MovDelay[x][y]) /* start new growing cycle */
9022 if (DelayReached(&sound_delay, sound_delay_value))
9024 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9025 sound_delay_value = 30;
9029 if (MovDelay[x][y]) /* wait some time before growing bigger */
9032 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9034 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9035 6 - MovDelay[x][y]);
9037 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9040 if (!MovDelay[x][y])
9042 Feld[x][y] = Store[x][y];
9044 DrawLevelField(x, y);
9049 void AmoebaDisappearing(int x, int y)
9051 static unsigned long sound_delay = 0;
9052 static unsigned long sound_delay_value = 0;
9054 if (!MovDelay[x][y]) /* start new shrinking cycle */
9058 if (DelayReached(&sound_delay, sound_delay_value))
9059 sound_delay_value = 30;
9062 if (MovDelay[x][y]) /* wait some time before shrinking */
9065 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9067 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9068 6 - MovDelay[x][y]);
9070 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9073 if (!MovDelay[x][y])
9075 Feld[x][y] = EL_EMPTY;
9076 DrawLevelField(x, y);
9078 /* don't let mole enter this field in this cycle;
9079 (give priority to objects falling to this field from above) */
9085 void AmoebeAbleger(int ax, int ay)
9088 int element = Feld[ax][ay];
9089 int graphic = el2img(element);
9090 int newax = ax, neway = ay;
9091 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9092 static int xy[4][2] =
9100 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9102 Feld[ax][ay] = EL_AMOEBA_DEAD;
9103 DrawLevelField(ax, ay);
9107 if (IS_ANIMATED(graphic))
9108 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9110 if (!MovDelay[ax][ay]) /* start making new amoeba field */
9111 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9113 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
9116 if (MovDelay[ax][ay])
9120 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9123 int x = ax + xy[start][0];
9124 int y = ay + xy[start][1];
9126 if (!IN_LEV_FIELD(x, y))
9129 if (IS_FREE(x, y) ||
9130 CAN_GROW_INTO(Feld[x][y]) ||
9131 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9132 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9138 if (newax == ax && neway == ay)
9141 else /* normal or "filled" (BD style) amoeba */
9144 boolean waiting_for_player = FALSE;
9146 for (i = 0; i < NUM_DIRECTIONS; i++)
9148 int j = (start + i) % 4;
9149 int x = ax + xy[j][0];
9150 int y = ay + xy[j][1];
9152 if (!IN_LEV_FIELD(x, y))
9155 if (IS_FREE(x, y) ||
9156 CAN_GROW_INTO(Feld[x][y]) ||
9157 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9158 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9164 else if (IS_PLAYER(x, y))
9165 waiting_for_player = TRUE;
9168 if (newax == ax && neway == ay) /* amoeba cannot grow */
9170 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9172 Feld[ax][ay] = EL_AMOEBA_DEAD;
9173 DrawLevelField(ax, ay);
9174 AmoebaCnt[AmoebaNr[ax][ay]]--;
9176 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
9178 if (element == EL_AMOEBA_FULL)
9179 AmoebeUmwandeln(ax, ay);
9180 else if (element == EL_BD_AMOEBA)
9181 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9186 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9188 /* amoeba gets larger by growing in some direction */
9190 int new_group_nr = AmoebaNr[ax][ay];
9193 if (new_group_nr == 0)
9195 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9196 printf("AmoebeAbleger(): This should never happen!\n");
9201 AmoebaNr[newax][neway] = new_group_nr;
9202 AmoebaCnt[new_group_nr]++;
9203 AmoebaCnt2[new_group_nr]++;
9205 /* if amoeba touches other amoeba(s) after growing, unify them */
9206 AmoebenVereinigen(newax, neway);
9208 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9210 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9216 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9217 (neway == lev_fieldy - 1 && newax != ax))
9219 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
9220 Store[newax][neway] = element;
9222 else if (neway == ay || element == EL_EMC_DRIPPER)
9224 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
9226 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9230 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
9231 Feld[ax][ay] = EL_AMOEBA_DROPPING;
9232 Store[ax][ay] = EL_AMOEBA_DROP;
9233 ContinueMoving(ax, ay);
9237 DrawLevelField(newax, neway);
9240 void Life(int ax, int ay)
9244 int element = Feld[ax][ay];
9245 int graphic = el2img(element);
9246 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9248 boolean changed = FALSE;
9250 if (IS_ANIMATED(graphic))
9251 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9256 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
9257 MovDelay[ax][ay] = life_time;
9259 if (MovDelay[ax][ay]) /* wait some time before next cycle */
9262 if (MovDelay[ax][ay])
9266 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9268 int xx = ax+x1, yy = ay+y1;
9271 if (!IN_LEV_FIELD(xx, yy))
9274 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9276 int x = xx+x2, y = yy+y2;
9278 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9281 if (((Feld[x][y] == element ||
9282 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9284 (IS_FREE(x, y) && Stop[x][y]))
9288 if (xx == ax && yy == ay) /* field in the middle */
9290 if (nachbarn < life_parameter[0] ||
9291 nachbarn > life_parameter[1])
9293 Feld[xx][yy] = EL_EMPTY;
9295 DrawLevelField(xx, yy);
9296 Stop[xx][yy] = TRUE;
9300 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9301 { /* free border field */
9302 if (nachbarn >= life_parameter[2] &&
9303 nachbarn <= life_parameter[3])
9305 Feld[xx][yy] = element;
9306 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9308 DrawLevelField(xx, yy);
9309 Stop[xx][yy] = TRUE;
9316 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9317 SND_GAME_OF_LIFE_GROWING);
9320 static void InitRobotWheel(int x, int y)
9322 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9325 static void RunRobotWheel(int x, int y)
9327 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9330 static void StopRobotWheel(int x, int y)
9332 if (ZX == x && ZY == y)
9336 game.robot_wheel_active = FALSE;
9340 static void InitTimegateWheel(int x, int y)
9342 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9345 static void RunTimegateWheel(int x, int y)
9347 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9350 static void InitMagicBallDelay(int x, int y)
9353 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9355 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9359 static void ActivateMagicBall(int bx, int by)
9363 if (level.ball_random)
9365 int pos_border = RND(8); /* select one of the eight border elements */
9366 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9367 int xx = pos_content % 3;
9368 int yy = pos_content / 3;
9373 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9374 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9378 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9380 int xx = x - bx + 1;
9381 int yy = y - by + 1;
9383 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9384 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9388 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9391 void CheckExit(int x, int y)
9393 if (local_player->gems_still_needed > 0 ||
9394 local_player->sokobanfields_still_needed > 0 ||
9395 local_player->lights_still_needed > 0)
9397 int element = Feld[x][y];
9398 int graphic = el2img(element);
9400 if (IS_ANIMATED(graphic))
9401 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9406 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9409 Feld[x][y] = EL_EXIT_OPENING;
9411 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9414 void CheckExitEM(int x, int y)
9416 if (local_player->gems_still_needed > 0 ||
9417 local_player->sokobanfields_still_needed > 0 ||
9418 local_player->lights_still_needed > 0)
9420 int element = Feld[x][y];
9421 int graphic = el2img(element);
9423 if (IS_ANIMATED(graphic))
9424 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9429 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9432 Feld[x][y] = EL_EM_EXIT_OPENING;
9434 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9437 void CheckExitSteel(int x, int y)
9439 if (local_player->gems_still_needed > 0 ||
9440 local_player->sokobanfields_still_needed > 0 ||
9441 local_player->lights_still_needed > 0)
9443 int element = Feld[x][y];
9444 int graphic = el2img(element);
9446 if (IS_ANIMATED(graphic))
9447 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9452 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9455 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9457 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9460 void CheckExitSteelEM(int x, int y)
9462 if (local_player->gems_still_needed > 0 ||
9463 local_player->sokobanfields_still_needed > 0 ||
9464 local_player->lights_still_needed > 0)
9466 int element = Feld[x][y];
9467 int graphic = el2img(element);
9469 if (IS_ANIMATED(graphic))
9470 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9475 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9478 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9480 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9483 void CheckExitSP(int x, int y)
9485 if (local_player->gems_still_needed > 0)
9487 int element = Feld[x][y];
9488 int graphic = el2img(element);
9490 if (IS_ANIMATED(graphic))
9491 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9496 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9499 Feld[x][y] = EL_SP_EXIT_OPENING;
9501 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9504 static void CloseAllOpenTimegates()
9508 SCAN_PLAYFIELD(x, y)
9510 int element = Feld[x][y];
9512 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9514 Feld[x][y] = EL_TIMEGATE_CLOSING;
9516 PlayLevelSoundAction(x, y, ACTION_CLOSING);
9521 void DrawTwinkleOnField(int x, int y)
9523 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9526 if (Feld[x][y] == EL_BD_DIAMOND)
9529 if (MovDelay[x][y] == 0) /* next animation frame */
9530 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9532 if (MovDelay[x][y] != 0) /* wait some time before next frame */
9536 DrawLevelElementAnimation(x, y, Feld[x][y]);
9538 if (MovDelay[x][y] != 0)
9540 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9541 10 - MovDelay[x][y]);
9543 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9548 void MauerWaechst(int x, int y)
9552 if (!MovDelay[x][y]) /* next animation frame */
9553 MovDelay[x][y] = 3 * delay;
9555 if (MovDelay[x][y]) /* wait some time before next frame */
9559 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9561 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9562 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9564 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9567 if (!MovDelay[x][y])
9569 if (MovDir[x][y] == MV_LEFT)
9571 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9572 DrawLevelField(x - 1, y);
9574 else if (MovDir[x][y] == MV_RIGHT)
9576 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9577 DrawLevelField(x + 1, y);
9579 else if (MovDir[x][y] == MV_UP)
9581 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9582 DrawLevelField(x, y - 1);
9586 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9587 DrawLevelField(x, y + 1);
9590 Feld[x][y] = Store[x][y];
9592 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9593 DrawLevelField(x, y);
9598 void MauerAbleger(int ax, int ay)
9600 int element = Feld[ax][ay];
9601 int graphic = el2img(element);
9602 boolean oben_frei = FALSE, unten_frei = FALSE;
9603 boolean links_frei = FALSE, rechts_frei = FALSE;
9604 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9605 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9606 boolean new_wall = FALSE;
9608 if (IS_ANIMATED(graphic))
9609 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9611 if (!MovDelay[ax][ay]) /* start building new wall */
9612 MovDelay[ax][ay] = 6;
9614 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9617 if (MovDelay[ax][ay])
9621 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9623 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9625 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9627 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9630 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9631 element == EL_EXPANDABLE_WALL_ANY)
9635 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9636 Store[ax][ay-1] = element;
9637 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9638 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9639 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9640 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9645 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9646 Store[ax][ay+1] = element;
9647 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9648 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9649 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9650 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9655 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9656 element == EL_EXPANDABLE_WALL_ANY ||
9657 element == EL_EXPANDABLE_WALL ||
9658 element == EL_BD_EXPANDABLE_WALL)
9662 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9663 Store[ax-1][ay] = element;
9664 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9665 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9666 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9667 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9673 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9674 Store[ax+1][ay] = element;
9675 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9676 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9677 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9678 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9683 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9684 DrawLevelField(ax, ay);
9686 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9688 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9689 unten_massiv = TRUE;
9690 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9691 links_massiv = TRUE;
9692 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9693 rechts_massiv = TRUE;
9695 if (((oben_massiv && unten_massiv) ||
9696 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9697 element == EL_EXPANDABLE_WALL) &&
9698 ((links_massiv && rechts_massiv) ||
9699 element == EL_EXPANDABLE_WALL_VERTICAL))
9700 Feld[ax][ay] = EL_WALL;
9703 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9706 void MauerAblegerStahl(int ax, int ay)
9708 int element = Feld[ax][ay];
9709 int graphic = el2img(element);
9710 boolean oben_frei = FALSE, unten_frei = FALSE;
9711 boolean links_frei = FALSE, rechts_frei = FALSE;
9712 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9713 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9714 boolean new_wall = FALSE;
9716 if (IS_ANIMATED(graphic))
9717 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9719 if (!MovDelay[ax][ay]) /* start building new wall */
9720 MovDelay[ax][ay] = 6;
9722 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9725 if (MovDelay[ax][ay])
9729 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9731 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9733 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9735 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9738 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9739 element == EL_EXPANDABLE_STEELWALL_ANY)
9743 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9744 Store[ax][ay-1] = element;
9745 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9746 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9747 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9748 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9753 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9754 Store[ax][ay+1] = element;
9755 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9756 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9757 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9758 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9763 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9764 element == EL_EXPANDABLE_STEELWALL_ANY)
9768 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9769 Store[ax-1][ay] = element;
9770 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9771 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9772 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9773 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9779 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9780 Store[ax+1][ay] = element;
9781 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9782 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9783 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9784 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9789 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9791 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9792 unten_massiv = TRUE;
9793 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9794 links_massiv = TRUE;
9795 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9796 rechts_massiv = TRUE;
9798 if (((oben_massiv && unten_massiv) ||
9799 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9800 ((links_massiv && rechts_massiv) ||
9801 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9802 Feld[ax][ay] = EL_WALL;
9805 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9808 void CheckForDragon(int x, int y)
9811 boolean dragon_found = FALSE;
9812 static int xy[4][2] =
9820 for (i = 0; i < NUM_DIRECTIONS; i++)
9822 for (j = 0; j < 4; j++)
9824 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9826 if (IN_LEV_FIELD(xx, yy) &&
9827 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9829 if (Feld[xx][yy] == EL_DRAGON)
9830 dragon_found = TRUE;
9839 for (i = 0; i < NUM_DIRECTIONS; i++)
9841 for (j = 0; j < 3; j++)
9843 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9845 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9847 Feld[xx][yy] = EL_EMPTY;
9848 DrawLevelField(xx, yy);
9857 static void InitBuggyBase(int x, int y)
9859 int element = Feld[x][y];
9860 int activating_delay = FRAMES_PER_SECOND / 4;
9863 (element == EL_SP_BUGGY_BASE ?
9864 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9865 element == EL_SP_BUGGY_BASE_ACTIVATING ?
9867 element == EL_SP_BUGGY_BASE_ACTIVE ?
9868 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9871 static void WarnBuggyBase(int x, int y)
9874 static int xy[4][2] =
9882 for (i = 0; i < NUM_DIRECTIONS; i++)
9884 int xx = x + xy[i][0];
9885 int yy = y + xy[i][1];
9887 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9889 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9896 static void InitTrap(int x, int y)
9898 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9901 static void ActivateTrap(int x, int y)
9903 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9906 static void ChangeActiveTrap(int x, int y)
9908 int graphic = IMG_TRAP_ACTIVE;
9910 /* if new animation frame was drawn, correct crumbled sand border */
9911 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9912 DrawLevelFieldCrumbledSand(x, y);
9915 static int getSpecialActionElement(int element, int number, int base_element)
9917 return (element != EL_EMPTY ? element :
9918 number != -1 ? base_element + number - 1 :
9922 static int getModifiedActionNumber(int value_old, int operator, int operand,
9923 int value_min, int value_max)
9925 int value_new = (operator == CA_MODE_SET ? operand :
9926 operator == CA_MODE_ADD ? value_old + operand :
9927 operator == CA_MODE_SUBTRACT ? value_old - operand :
9928 operator == CA_MODE_MULTIPLY ? value_old * operand :
9929 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
9930 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
9933 return (value_new < value_min ? value_min :
9934 value_new > value_max ? value_max :
9938 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9940 struct ElementInfo *ei = &element_info[element];
9941 struct ElementChangeInfo *change = &ei->change_page[page];
9942 int target_element = change->target_element;
9943 int action_type = change->action_type;
9944 int action_mode = change->action_mode;
9945 int action_arg = change->action_arg;
9948 if (!change->has_action)
9951 /* ---------- determine action paramater values -------------------------- */
9953 int level_time_value =
9954 (level.time > 0 ? TimeLeft :
9957 int action_arg_element =
9958 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
9959 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9960 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
9963 int action_arg_direction =
9964 (action_arg >= CA_ARG_DIRECTION_LEFT &&
9965 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9966 action_arg == CA_ARG_DIRECTION_TRIGGER ?
9967 change->actual_trigger_side :
9968 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9969 MV_DIR_OPPOSITE(change->actual_trigger_side) :
9972 int action_arg_number_min =
9973 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9976 int action_arg_number_max =
9977 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9978 action_type == CA_SET_LEVEL_GEMS ? 999 :
9979 action_type == CA_SET_LEVEL_TIME ? 9999 :
9980 action_type == CA_SET_LEVEL_SCORE ? 99999 :
9981 action_type == CA_SET_CE_VALUE ? 9999 :
9982 action_type == CA_SET_CE_SCORE ? 9999 :
9985 int action_arg_number_reset =
9986 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9987 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9988 action_type == CA_SET_LEVEL_TIME ? level.time :
9989 action_type == CA_SET_LEVEL_SCORE ? 0 :
9990 #if USE_NEW_CUSTOM_VALUE
9991 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9993 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
9995 action_type == CA_SET_CE_SCORE ? 0 :
9998 int action_arg_number =
9999 (action_arg <= CA_ARG_MAX ? action_arg :
10000 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10001 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10002 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10003 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10004 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10005 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10006 #if USE_NEW_CUSTOM_VALUE
10007 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10009 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10011 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10012 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10013 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10014 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10015 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10016 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10017 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10018 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10019 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10020 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
10021 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10024 int action_arg_number_old =
10025 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10026 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10027 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10028 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10029 action_type == CA_SET_CE_SCORE ? ei->collect_score :
10032 int action_arg_number_new =
10033 getModifiedActionNumber(action_arg_number_old,
10034 action_mode, action_arg_number,
10035 action_arg_number_min, action_arg_number_max);
10038 int trigger_player_bits = change->actual_trigger_player_bits;
10040 int trigger_player_bits =
10041 (change->actual_trigger_player >= EL_PLAYER_1 &&
10042 change->actual_trigger_player <= EL_PLAYER_4 ?
10043 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10047 int action_arg_player_bits =
10048 (action_arg >= CA_ARG_PLAYER_1 &&
10049 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10050 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10053 /* ---------- execute action -------------------------------------------- */
10055 switch (action_type)
10062 /* ---------- level actions ------------------------------------------- */
10064 case CA_RESTART_LEVEL:
10066 game.restart_level = TRUE;
10071 case CA_SHOW_ENVELOPE:
10073 int element = getSpecialActionElement(action_arg_element,
10074 action_arg_number, EL_ENVELOPE_1);
10076 if (IS_ENVELOPE(element))
10077 local_player->show_envelope = element;
10082 case CA_SET_LEVEL_TIME:
10084 if (level.time > 0) /* only modify limited time value */
10086 TimeLeft = action_arg_number_new;
10089 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10091 DisplayGameControlValues();
10093 DrawGameValue_Time(TimeLeft);
10096 if (!TimeLeft && setup.time_limit)
10097 for (i = 0; i < MAX_PLAYERS; i++)
10098 KillPlayer(&stored_player[i]);
10104 case CA_SET_LEVEL_SCORE:
10106 local_player->score = action_arg_number_new;
10109 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10111 DisplayGameControlValues();
10113 DrawGameValue_Score(local_player->score);
10119 case CA_SET_LEVEL_GEMS:
10121 local_player->gems_still_needed = action_arg_number_new;
10124 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10126 DisplayGameControlValues();
10128 DrawGameValue_Emeralds(local_player->gems_still_needed);
10134 #if !USE_PLAYER_GRAVITY
10135 case CA_SET_LEVEL_GRAVITY:
10137 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10138 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10139 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10145 case CA_SET_LEVEL_WIND:
10147 game.wind_direction = action_arg_direction;
10152 /* ---------- player actions ------------------------------------------ */
10154 case CA_MOVE_PLAYER:
10156 /* automatically move to the next field in specified direction */
10157 for (i = 0; i < MAX_PLAYERS; i++)
10158 if (trigger_player_bits & (1 << i))
10159 stored_player[i].programmed_action = action_arg_direction;
10164 case CA_EXIT_PLAYER:
10166 for (i = 0; i < MAX_PLAYERS; i++)
10167 if (action_arg_player_bits & (1 << i))
10168 PlayerWins(&stored_player[i]);
10173 case CA_KILL_PLAYER:
10175 for (i = 0; i < MAX_PLAYERS; i++)
10176 if (action_arg_player_bits & (1 << i))
10177 KillPlayer(&stored_player[i]);
10182 case CA_SET_PLAYER_KEYS:
10184 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10185 int element = getSpecialActionElement(action_arg_element,
10186 action_arg_number, EL_KEY_1);
10188 if (IS_KEY(element))
10190 for (i = 0; i < MAX_PLAYERS; i++)
10192 if (trigger_player_bits & (1 << i))
10194 stored_player[i].key[KEY_NR(element)] = key_state;
10196 DrawGameDoorValues();
10204 case CA_SET_PLAYER_SPEED:
10206 for (i = 0; i < MAX_PLAYERS; i++)
10208 if (trigger_player_bits & (1 << i))
10210 int move_stepsize = TILEX / stored_player[i].move_delay_value;
10212 if (action_arg == CA_ARG_SPEED_FASTER &&
10213 stored_player[i].cannot_move)
10215 action_arg_number = STEPSIZE_VERY_SLOW;
10217 else if (action_arg == CA_ARG_SPEED_SLOWER ||
10218 action_arg == CA_ARG_SPEED_FASTER)
10220 action_arg_number = 2;
10221 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10224 else if (action_arg == CA_ARG_NUMBER_RESET)
10226 action_arg_number = level.initial_player_stepsize[i];
10230 getModifiedActionNumber(move_stepsize,
10233 action_arg_number_min,
10234 action_arg_number_max);
10236 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10243 case CA_SET_PLAYER_SHIELD:
10245 for (i = 0; i < MAX_PLAYERS; i++)
10247 if (trigger_player_bits & (1 << i))
10249 if (action_arg == CA_ARG_SHIELD_OFF)
10251 stored_player[i].shield_normal_time_left = 0;
10252 stored_player[i].shield_deadly_time_left = 0;
10254 else if (action_arg == CA_ARG_SHIELD_NORMAL)
10256 stored_player[i].shield_normal_time_left = 999999;
10258 else if (action_arg == CA_ARG_SHIELD_DEADLY)
10260 stored_player[i].shield_normal_time_left = 999999;
10261 stored_player[i].shield_deadly_time_left = 999999;
10269 #if USE_PLAYER_GRAVITY
10270 case CA_SET_PLAYER_GRAVITY:
10272 for (i = 0; i < MAX_PLAYERS; i++)
10274 if (trigger_player_bits & (1 << i))
10276 stored_player[i].gravity =
10277 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10278 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10279 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10280 stored_player[i].gravity);
10288 case CA_SET_PLAYER_ARTWORK:
10290 for (i = 0; i < MAX_PLAYERS; i++)
10292 if (trigger_player_bits & (1 << i))
10294 int artwork_element = action_arg_element;
10296 if (action_arg == CA_ARG_ELEMENT_RESET)
10298 (level.use_artwork_element[i] ? level.artwork_element[i] :
10299 stored_player[i].element_nr);
10301 #if USE_GFX_RESET_PLAYER_ARTWORK
10302 if (stored_player[i].artwork_element != artwork_element)
10303 stored_player[i].Frame = 0;
10306 stored_player[i].artwork_element = artwork_element;
10308 SetPlayerWaiting(&stored_player[i], FALSE);
10310 /* set number of special actions for bored and sleeping animation */
10311 stored_player[i].num_special_action_bored =
10312 get_num_special_action(artwork_element,
10313 ACTION_BORING_1, ACTION_BORING_LAST);
10314 stored_player[i].num_special_action_sleeping =
10315 get_num_special_action(artwork_element,
10316 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10323 /* ---------- CE actions ---------------------------------------------- */
10325 case CA_SET_CE_VALUE:
10327 #if USE_NEW_CUSTOM_VALUE
10328 int last_ce_value = CustomValue[x][y];
10330 CustomValue[x][y] = action_arg_number_new;
10332 if (CustomValue[x][y] != last_ce_value)
10334 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10335 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10337 if (CustomValue[x][y] == 0)
10339 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10340 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10348 case CA_SET_CE_SCORE:
10350 #if USE_NEW_CUSTOM_VALUE
10351 int last_ce_score = ei->collect_score;
10353 ei->collect_score = action_arg_number_new;
10355 if (ei->collect_score != last_ce_score)
10357 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10358 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10360 if (ei->collect_score == 0)
10364 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10365 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10368 This is a very special case that seems to be a mixture between
10369 CheckElementChange() and CheckTriggeredElementChange(): while
10370 the first one only affects single elements that are triggered
10371 directly, the second one affects multiple elements in the playfield
10372 that are triggered indirectly by another element. This is a third
10373 case: Changing the CE score always affects multiple identical CEs,
10374 so every affected CE must be checked, not only the single CE for
10375 which the CE score was changed in the first place (as every instance
10376 of that CE shares the same CE score, and therefore also can change)!
10378 SCAN_PLAYFIELD(xx, yy)
10380 if (Feld[xx][yy] == element)
10381 CheckElementChange(xx, yy, element, EL_UNDEFINED,
10382 CE_SCORE_GETS_ZERO);
10391 /* ---------- engine actions ------------------------------------------ */
10393 case CA_SET_ENGINE_SCAN_MODE:
10395 InitPlayfieldScanMode(action_arg);
10405 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10407 int old_element = Feld[x][y];
10408 int new_element = GetElementFromGroupElement(element);
10409 int previous_move_direction = MovDir[x][y];
10410 #if USE_NEW_CUSTOM_VALUE
10411 int last_ce_value = CustomValue[x][y];
10413 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10414 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10415 boolean add_player_onto_element = (new_element_is_player &&
10416 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
10417 /* this breaks SnakeBite when a snake is
10418 halfway through a door that closes */
10419 /* NOW FIXED AT LEVEL INIT IN files.c */
10420 new_element != EL_SOKOBAN_FIELD_PLAYER &&
10422 IS_WALKABLE(old_element));
10425 /* check if element under the player changes from accessible to unaccessible
10426 (needed for special case of dropping element which then changes) */
10427 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10428 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10436 if (!add_player_onto_element)
10438 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10439 RemoveMovingField(x, y);
10443 Feld[x][y] = new_element;
10445 #if !USE_GFX_RESET_GFX_ANIMATION
10446 ResetGfxAnimation(x, y);
10447 ResetRandomAnimationValue(x, y);
10450 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10451 MovDir[x][y] = previous_move_direction;
10453 #if USE_NEW_CUSTOM_VALUE
10454 if (element_info[new_element].use_last_ce_value)
10455 CustomValue[x][y] = last_ce_value;
10458 InitField_WithBug1(x, y, FALSE);
10460 new_element = Feld[x][y]; /* element may have changed */
10462 #if USE_GFX_RESET_GFX_ANIMATION
10463 ResetGfxAnimation(x, y);
10464 ResetRandomAnimationValue(x, y);
10467 DrawLevelField(x, y);
10469 if (GFX_CRUMBLED(new_element))
10470 DrawLevelFieldCrumbledSandNeighbours(x, y);
10474 /* check if element under the player changes from accessible to unaccessible
10475 (needed for special case of dropping element which then changes) */
10476 /* (must be checked after creating new element for walkable group elements) */
10477 #if USE_FIX_KILLED_BY_NON_WALKABLE
10478 if (IS_PLAYER(x, y) && !player_explosion_protected &&
10479 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10486 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10487 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10496 /* "ChangeCount" not set yet to allow "entered by player" change one time */
10497 if (new_element_is_player)
10498 RelocatePlayer(x, y, new_element);
10501 ChangeCount[x][y]++; /* count number of changes in the same frame */
10503 TestIfBadThingTouchesPlayer(x, y);
10504 TestIfPlayerTouchesCustomElement(x, y);
10505 TestIfElementTouchesCustomElement(x, y);
10508 static void CreateField(int x, int y, int element)
10510 CreateFieldExt(x, y, element, FALSE);
10513 static void CreateElementFromChange(int x, int y, int element)
10515 element = GET_VALID_RUNTIME_ELEMENT(element);
10517 #if USE_STOP_CHANGED_ELEMENTS
10518 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10520 int old_element = Feld[x][y];
10522 /* prevent changed element from moving in same engine frame
10523 unless both old and new element can either fall or move */
10524 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10525 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10530 CreateFieldExt(x, y, element, TRUE);
10533 static boolean ChangeElement(int x, int y, int element, int page)
10535 struct ElementInfo *ei = &element_info[element];
10536 struct ElementChangeInfo *change = &ei->change_page[page];
10537 int ce_value = CustomValue[x][y];
10538 int ce_score = ei->collect_score;
10539 int target_element;
10540 int old_element = Feld[x][y];
10542 /* always use default change event to prevent running into a loop */
10543 if (ChangeEvent[x][y] == -1)
10544 ChangeEvent[x][y] = CE_DELAY;
10546 if (ChangeEvent[x][y] == CE_DELAY)
10548 /* reset actual trigger element, trigger player and action element */
10549 change->actual_trigger_element = EL_EMPTY;
10550 change->actual_trigger_player = EL_PLAYER_1;
10551 change->actual_trigger_player_bits = CH_PLAYER_1;
10552 change->actual_trigger_side = CH_SIDE_NONE;
10553 change->actual_trigger_ce_value = 0;
10554 change->actual_trigger_ce_score = 0;
10557 /* do not change elements more than a specified maximum number of changes */
10558 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10561 ChangeCount[x][y]++; /* count number of changes in the same frame */
10563 if (change->explode)
10570 if (change->use_target_content)
10572 boolean complete_replace = TRUE;
10573 boolean can_replace[3][3];
10576 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10579 boolean is_walkable;
10580 boolean is_diggable;
10581 boolean is_collectible;
10582 boolean is_removable;
10583 boolean is_destructible;
10584 int ex = x + xx - 1;
10585 int ey = y + yy - 1;
10586 int content_element = change->target_content.e[xx][yy];
10589 can_replace[xx][yy] = TRUE;
10591 if (ex == x && ey == y) /* do not check changing element itself */
10594 if (content_element == EL_EMPTY_SPACE)
10596 can_replace[xx][yy] = FALSE; /* do not replace border with space */
10601 if (!IN_LEV_FIELD(ex, ey))
10603 can_replace[xx][yy] = FALSE;
10604 complete_replace = FALSE;
10611 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10612 e = MovingOrBlocked2Element(ex, ey);
10614 is_empty = (IS_FREE(ex, ey) ||
10615 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10617 is_walkable = (is_empty || IS_WALKABLE(e));
10618 is_diggable = (is_empty || IS_DIGGABLE(e));
10619 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10620 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10621 is_removable = (is_diggable || is_collectible);
10623 can_replace[xx][yy] =
10624 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10625 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10626 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10627 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10628 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10629 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10630 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10632 if (!can_replace[xx][yy])
10633 complete_replace = FALSE;
10636 if (!change->only_if_complete || complete_replace)
10638 boolean something_has_changed = FALSE;
10640 if (change->only_if_complete && change->use_random_replace &&
10641 RND(100) < change->random_percentage)
10644 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10646 int ex = x + xx - 1;
10647 int ey = y + yy - 1;
10648 int content_element;
10650 if (can_replace[xx][yy] && (!change->use_random_replace ||
10651 RND(100) < change->random_percentage))
10653 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10654 RemoveMovingField(ex, ey);
10656 ChangeEvent[ex][ey] = ChangeEvent[x][y];
10658 content_element = change->target_content.e[xx][yy];
10659 target_element = GET_TARGET_ELEMENT(element, content_element, change,
10660 ce_value, ce_score);
10662 CreateElementFromChange(ex, ey, target_element);
10664 something_has_changed = TRUE;
10666 /* for symmetry reasons, freeze newly created border elements */
10667 if (ex != x || ey != y)
10668 Stop[ex][ey] = TRUE; /* no more moving in this frame */
10672 if (something_has_changed)
10674 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10675 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10681 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10682 ce_value, ce_score);
10684 if (element == EL_DIAGONAL_GROWING ||
10685 element == EL_DIAGONAL_SHRINKING)
10687 target_element = Store[x][y];
10689 Store[x][y] = EL_EMPTY;
10692 CreateElementFromChange(x, y, target_element);
10694 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10695 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10698 /* this uses direct change before indirect change */
10699 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10704 #if USE_NEW_DELAYED_ACTION
10706 static void HandleElementChange(int x, int y, int page)
10708 int element = MovingOrBlocked2Element(x, y);
10709 struct ElementInfo *ei = &element_info[element];
10710 struct ElementChangeInfo *change = &ei->change_page[page];
10713 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10714 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10717 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10718 x, y, element, element_info[element].token_name);
10719 printf("HandleElementChange(): This should never happen!\n");
10724 /* this can happen with classic bombs on walkable, changing elements */
10725 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10728 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
10729 ChangeDelay[x][y] = 0;
10735 if (ChangeDelay[x][y] == 0) /* initialize element change */
10737 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10739 if (change->can_change)
10742 /* !!! not clear why graphic animation should be reset at all here !!! */
10743 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10744 #if USE_GFX_RESET_WHEN_NOT_MOVING
10745 /* when a custom element is about to change (for example by change delay),
10746 do not reset graphic animation when the custom element is moving */
10747 if (!IS_MOVING(x, y))
10750 ResetGfxAnimation(x, y);
10751 ResetRandomAnimationValue(x, y);
10755 if (change->pre_change_function)
10756 change->pre_change_function(x, y);
10760 ChangeDelay[x][y]--;
10762 if (ChangeDelay[x][y] != 0) /* continue element change */
10764 if (change->can_change)
10766 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10768 if (IS_ANIMATED(graphic))
10769 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10771 if (change->change_function)
10772 change->change_function(x, y);
10775 else /* finish element change */
10777 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10779 page = ChangePage[x][y];
10780 ChangePage[x][y] = -1;
10782 change = &ei->change_page[page];
10785 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10787 ChangeDelay[x][y] = 1; /* try change after next move step */
10788 ChangePage[x][y] = page; /* remember page to use for change */
10793 if (change->can_change)
10795 if (ChangeElement(x, y, element, page))
10797 if (change->post_change_function)
10798 change->post_change_function(x, y);
10802 if (change->has_action)
10803 ExecuteCustomElementAction(x, y, element, page);
10809 static void HandleElementChange(int x, int y, int page)
10811 int element = MovingOrBlocked2Element(x, y);
10812 struct ElementInfo *ei = &element_info[element];
10813 struct ElementChangeInfo *change = &ei->change_page[page];
10816 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
10819 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10820 x, y, element, element_info[element].token_name);
10821 printf("HandleElementChange(): This should never happen!\n");
10826 /* this can happen with classic bombs on walkable, changing elements */
10827 if (!CAN_CHANGE(element))
10830 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
10831 ChangeDelay[x][y] = 0;
10837 if (ChangeDelay[x][y] == 0) /* initialize element change */
10839 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10841 ResetGfxAnimation(x, y);
10842 ResetRandomAnimationValue(x, y);
10844 if (change->pre_change_function)
10845 change->pre_change_function(x, y);
10848 ChangeDelay[x][y]--;
10850 if (ChangeDelay[x][y] != 0) /* continue element change */
10852 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10854 if (IS_ANIMATED(graphic))
10855 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10857 if (change->change_function)
10858 change->change_function(x, y);
10860 else /* finish element change */
10862 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10864 page = ChangePage[x][y];
10865 ChangePage[x][y] = -1;
10867 change = &ei->change_page[page];
10870 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10872 ChangeDelay[x][y] = 1; /* try change after next move step */
10873 ChangePage[x][y] = page; /* remember page to use for change */
10878 if (ChangeElement(x, y, element, page))
10880 if (change->post_change_function)
10881 change->post_change_function(x, y);
10888 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10889 int trigger_element,
10891 int trigger_player,
10895 boolean change_done_any = FALSE;
10896 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10899 if (!(trigger_events[trigger_element][trigger_event]))
10903 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10904 trigger_event, recursion_loop_depth, recursion_loop_detected,
10905 recursion_loop_element, EL_NAME(recursion_loop_element));
10908 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10910 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10912 int element = EL_CUSTOM_START + i;
10913 boolean change_done = FALSE;
10916 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10917 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10920 for (p = 0; p < element_info[element].num_change_pages; p++)
10922 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10924 if (change->can_change_or_has_action &&
10925 change->has_event[trigger_event] &&
10926 change->trigger_side & trigger_side &&
10927 change->trigger_player & trigger_player &&
10928 change->trigger_page & trigger_page_bits &&
10929 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10931 change->actual_trigger_element = trigger_element;
10932 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10933 change->actual_trigger_player_bits = trigger_player;
10934 change->actual_trigger_side = trigger_side;
10935 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10936 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10938 if ((change->can_change && !change_done) || change->has_action)
10942 SCAN_PLAYFIELD(x, y)
10944 if (Feld[x][y] == element)
10946 if (change->can_change && !change_done)
10948 ChangeDelay[x][y] = 1;
10949 ChangeEvent[x][y] = trigger_event;
10951 HandleElementChange(x, y, p);
10953 #if USE_NEW_DELAYED_ACTION
10954 else if (change->has_action)
10956 ExecuteCustomElementAction(x, y, element, p);
10957 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10960 if (change->has_action)
10962 ExecuteCustomElementAction(x, y, element, p);
10963 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10969 if (change->can_change)
10971 change_done = TRUE;
10972 change_done_any = TRUE;
10979 RECURSION_LOOP_DETECTION_END();
10981 return change_done_any;
10984 static boolean CheckElementChangeExt(int x, int y,
10986 int trigger_element,
10988 int trigger_player,
10991 boolean change_done = FALSE;
10994 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10995 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10998 if (Feld[x][y] == EL_BLOCKED)
11000 Blocked2Moving(x, y, &x, &y);
11001 element = Feld[x][y];
11005 /* check if element has already changed */
11006 if (Feld[x][y] != element)
11009 /* check if element has already changed or is about to change after moving */
11010 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11011 Feld[x][y] != element) ||
11013 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11014 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11015 ChangePage[x][y] != -1)))
11020 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11021 trigger_event, recursion_loop_depth, recursion_loop_detected,
11022 recursion_loop_element, EL_NAME(recursion_loop_element));
11025 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11027 for (p = 0; p < element_info[element].num_change_pages; p++)
11029 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11031 /* check trigger element for all events where the element that is checked
11032 for changing interacts with a directly adjacent element -- this is
11033 different to element changes that affect other elements to change on the
11034 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11035 boolean check_trigger_element =
11036 (trigger_event == CE_TOUCHING_X ||
11037 trigger_event == CE_HITTING_X ||
11038 trigger_event == CE_HIT_BY_X ||
11040 /* this one was forgotten until 3.2.3 */
11041 trigger_event == CE_DIGGING_X);
11044 if (change->can_change_or_has_action &&
11045 change->has_event[trigger_event] &&
11046 change->trigger_side & trigger_side &&
11047 change->trigger_player & trigger_player &&
11048 (!check_trigger_element ||
11049 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11051 change->actual_trigger_element = trigger_element;
11052 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11053 change->actual_trigger_player_bits = trigger_player;
11054 change->actual_trigger_side = trigger_side;
11055 change->actual_trigger_ce_value = CustomValue[x][y];
11056 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11058 /* special case: trigger element not at (x,y) position for some events */
11059 if (check_trigger_element)
11071 { 0, 0 }, { 0, 0 }, { 0, 0 },
11075 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11076 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11078 change->actual_trigger_ce_value = CustomValue[xx][yy];
11079 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11082 if (change->can_change && !change_done)
11084 ChangeDelay[x][y] = 1;
11085 ChangeEvent[x][y] = trigger_event;
11087 HandleElementChange(x, y, p);
11089 change_done = TRUE;
11091 #if USE_NEW_DELAYED_ACTION
11092 else if (change->has_action)
11094 ExecuteCustomElementAction(x, y, element, p);
11095 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11098 if (change->has_action)
11100 ExecuteCustomElementAction(x, y, element, p);
11101 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11107 RECURSION_LOOP_DETECTION_END();
11109 return change_done;
11112 static void PlayPlayerSound(struct PlayerInfo *player)
11114 int jx = player->jx, jy = player->jy;
11115 int sound_element = player->artwork_element;
11116 int last_action = player->last_action_waiting;
11117 int action = player->action_waiting;
11119 if (player->is_waiting)
11121 if (action != last_action)
11122 PlayLevelSoundElementAction(jx, jy, sound_element, action);
11124 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11128 if (action != last_action)
11129 StopSound(element_info[sound_element].sound[last_action]);
11131 if (last_action == ACTION_SLEEPING)
11132 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11136 static void PlayAllPlayersSound()
11140 for (i = 0; i < MAX_PLAYERS; i++)
11141 if (stored_player[i].active)
11142 PlayPlayerSound(&stored_player[i]);
11145 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11147 boolean last_waiting = player->is_waiting;
11148 int move_dir = player->MovDir;
11150 player->dir_waiting = move_dir;
11151 player->last_action_waiting = player->action_waiting;
11155 if (!last_waiting) /* not waiting -> waiting */
11157 player->is_waiting = TRUE;
11159 player->frame_counter_bored =
11161 game.player_boring_delay_fixed +
11162 GetSimpleRandom(game.player_boring_delay_random);
11163 player->frame_counter_sleeping =
11165 game.player_sleeping_delay_fixed +
11166 GetSimpleRandom(game.player_sleeping_delay_random);
11168 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11171 if (game.player_sleeping_delay_fixed +
11172 game.player_sleeping_delay_random > 0 &&
11173 player->anim_delay_counter == 0 &&
11174 player->post_delay_counter == 0 &&
11175 FrameCounter >= player->frame_counter_sleeping)
11176 player->is_sleeping = TRUE;
11177 else if (game.player_boring_delay_fixed +
11178 game.player_boring_delay_random > 0 &&
11179 FrameCounter >= player->frame_counter_bored)
11180 player->is_bored = TRUE;
11182 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11183 player->is_bored ? ACTION_BORING :
11186 if (player->is_sleeping && player->use_murphy)
11188 /* special case for sleeping Murphy when leaning against non-free tile */
11190 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11191 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11192 !IS_MOVING(player->jx - 1, player->jy)))
11193 move_dir = MV_LEFT;
11194 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11195 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11196 !IS_MOVING(player->jx + 1, player->jy)))
11197 move_dir = MV_RIGHT;
11199 player->is_sleeping = FALSE;
11201 player->dir_waiting = move_dir;
11204 if (player->is_sleeping)
11206 if (player->num_special_action_sleeping > 0)
11208 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11210 int last_special_action = player->special_action_sleeping;
11211 int num_special_action = player->num_special_action_sleeping;
11212 int special_action =
11213 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11214 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11215 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11216 last_special_action + 1 : ACTION_SLEEPING);
11217 int special_graphic =
11218 el_act_dir2img(player->artwork_element, special_action, move_dir);
11220 player->anim_delay_counter =
11221 graphic_info[special_graphic].anim_delay_fixed +
11222 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11223 player->post_delay_counter =
11224 graphic_info[special_graphic].post_delay_fixed +
11225 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11227 player->special_action_sleeping = special_action;
11230 if (player->anim_delay_counter > 0)
11232 player->action_waiting = player->special_action_sleeping;
11233 player->anim_delay_counter--;
11235 else if (player->post_delay_counter > 0)
11237 player->post_delay_counter--;
11241 else if (player->is_bored)
11243 if (player->num_special_action_bored > 0)
11245 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11247 int special_action =
11248 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11249 int special_graphic =
11250 el_act_dir2img(player->artwork_element, special_action, move_dir);
11252 player->anim_delay_counter =
11253 graphic_info[special_graphic].anim_delay_fixed +
11254 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11255 player->post_delay_counter =
11256 graphic_info[special_graphic].post_delay_fixed +
11257 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11259 player->special_action_bored = special_action;
11262 if (player->anim_delay_counter > 0)
11264 player->action_waiting = player->special_action_bored;
11265 player->anim_delay_counter--;
11267 else if (player->post_delay_counter > 0)
11269 player->post_delay_counter--;
11274 else if (last_waiting) /* waiting -> not waiting */
11276 player->is_waiting = FALSE;
11277 player->is_bored = FALSE;
11278 player->is_sleeping = FALSE;
11280 player->frame_counter_bored = -1;
11281 player->frame_counter_sleeping = -1;
11283 player->anim_delay_counter = 0;
11284 player->post_delay_counter = 0;
11286 player->dir_waiting = player->MovDir;
11287 player->action_waiting = ACTION_DEFAULT;
11289 player->special_action_bored = ACTION_DEFAULT;
11290 player->special_action_sleeping = ACTION_DEFAULT;
11294 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11296 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11297 int left = player_action & JOY_LEFT;
11298 int right = player_action & JOY_RIGHT;
11299 int up = player_action & JOY_UP;
11300 int down = player_action & JOY_DOWN;
11301 int button1 = player_action & JOY_BUTTON_1;
11302 int button2 = player_action & JOY_BUTTON_2;
11303 int dx = (left ? -1 : right ? 1 : 0);
11304 int dy = (up ? -1 : down ? 1 : 0);
11306 if (!player->active || tape.pausing)
11312 snapped = SnapField(player, dx, dy);
11316 dropped = DropElement(player);
11318 moved = MovePlayer(player, dx, dy);
11321 if (tape.single_step && tape.recording && !tape.pausing)
11323 if (button1 || (dropped && !moved))
11325 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11326 SnapField(player, 0, 0); /* stop snapping */
11330 SetPlayerWaiting(player, FALSE);
11332 return player_action;
11336 /* no actions for this player (no input at player's configured device) */
11338 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11339 SnapField(player, 0, 0);
11340 CheckGravityMovementWhenNotMoving(player);
11342 if (player->MovPos == 0)
11343 SetPlayerWaiting(player, TRUE);
11345 if (player->MovPos == 0) /* needed for tape.playing */
11346 player->is_moving = FALSE;
11348 player->is_dropping = FALSE;
11349 player->is_dropping_pressed = FALSE;
11350 player->drop_pressed_delay = 0;
11356 static void CheckLevelTime()
11360 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11362 if (level.native_em_level->lev->home == 0) /* all players at home */
11364 PlayerWins(local_player);
11366 AllPlayersGone = TRUE;
11368 level.native_em_level->lev->home = -1;
11371 if (level.native_em_level->ply[0]->alive == 0 &&
11372 level.native_em_level->ply[1]->alive == 0 &&
11373 level.native_em_level->ply[2]->alive == 0 &&
11374 level.native_em_level->ply[3]->alive == 0) /* all dead */
11375 AllPlayersGone = TRUE;
11378 if (TimeFrames >= FRAMES_PER_SECOND)
11383 for (i = 0; i < MAX_PLAYERS; i++)
11385 struct PlayerInfo *player = &stored_player[i];
11387 if (SHIELD_ON(player))
11389 player->shield_normal_time_left--;
11391 if (player->shield_deadly_time_left > 0)
11392 player->shield_deadly_time_left--;
11396 if (!local_player->LevelSolved && !level.use_step_counter)
11404 if (TimeLeft <= 10 && setup.time_limit)
11405 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11408 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11410 DisplayGameControlValues();
11412 DrawGameValue_Time(TimeLeft);
11415 if (!TimeLeft && setup.time_limit)
11417 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11418 level.native_em_level->lev->killed_out_of_time = TRUE;
11420 for (i = 0; i < MAX_PLAYERS; i++)
11421 KillPlayer(&stored_player[i]);
11425 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11427 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11429 DisplayGameControlValues();
11432 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11433 DrawGameValue_Time(TimePlayed);
11436 level.native_em_level->lev->time =
11437 (level.time == 0 ? TimePlayed : TimeLeft);
11440 if (tape.recording || tape.playing)
11441 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11445 UpdateAndDisplayGameControlValues();
11447 UpdateGameDoorValues();
11448 DrawGameDoorValues();
11452 void AdvanceFrameAndPlayerCounters(int player_nr)
11456 /* advance frame counters (global frame counter and time frame counter) */
11460 /* advance player counters (counters for move delay, move animation etc.) */
11461 for (i = 0; i < MAX_PLAYERS; i++)
11463 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11464 int move_delay_value = stored_player[i].move_delay_value;
11465 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11467 if (!advance_player_counters) /* not all players may be affected */
11470 #if USE_NEW_PLAYER_ANIM
11471 if (move_frames == 0) /* less than one move per game frame */
11473 int stepsize = TILEX / move_delay_value;
11474 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11475 int count = (stored_player[i].is_moving ?
11476 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11478 if (count % delay == 0)
11483 stored_player[i].Frame += move_frames;
11485 if (stored_player[i].MovPos != 0)
11486 stored_player[i].StepFrame += move_frames;
11488 if (stored_player[i].move_delay > 0)
11489 stored_player[i].move_delay--;
11491 /* due to bugs in previous versions, counter must count up, not down */
11492 if (stored_player[i].push_delay != -1)
11493 stored_player[i].push_delay++;
11495 if (stored_player[i].drop_delay > 0)
11496 stored_player[i].drop_delay--;
11498 if (stored_player[i].is_dropping_pressed)
11499 stored_player[i].drop_pressed_delay++;
11503 void StartGameActions(boolean init_network_game, boolean record_tape,
11506 unsigned long new_random_seed = InitRND(random_seed);
11509 TapeStartRecording(new_random_seed);
11511 #if defined(NETWORK_AVALIABLE)
11512 if (init_network_game)
11514 SendToServer_StartPlaying();
11525 static unsigned long game_frame_delay = 0;
11526 unsigned long game_frame_delay_value;
11527 byte *recorded_player_action;
11528 byte summarized_player_action = 0;
11529 byte tape_action[MAX_PLAYERS];
11532 /* detect endless loops, caused by custom element programming */
11533 if (recursion_loop_detected && recursion_loop_depth == 0)
11535 char *message = getStringCat3("Internal Error ! Element ",
11536 EL_NAME(recursion_loop_element),
11537 " caused endless loop ! Quit the game ?");
11539 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11540 EL_NAME(recursion_loop_element));
11542 RequestQuitGameExt(FALSE, level_editor_test_game, message);
11544 recursion_loop_detected = FALSE; /* if game should be continued */
11551 if (game.restart_level)
11552 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
11554 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11556 if (level.native_em_level->lev->home == 0) /* all players at home */
11558 PlayerWins(local_player);
11560 AllPlayersGone = TRUE;
11562 level.native_em_level->lev->home = -1;
11565 if (level.native_em_level->ply[0]->alive == 0 &&
11566 level.native_em_level->ply[1]->alive == 0 &&
11567 level.native_em_level->ply[2]->alive == 0 &&
11568 level.native_em_level->ply[3]->alive == 0) /* all dead */
11569 AllPlayersGone = TRUE;
11572 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11575 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11578 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
11581 game_frame_delay_value =
11582 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11584 if (tape.playing && tape.warp_forward && !tape.pausing)
11585 game_frame_delay_value = 0;
11587 /* ---------- main game synchronization point ---------- */
11589 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11591 if (network_playing && !network_player_action_received)
11593 /* try to get network player actions in time */
11595 #if defined(NETWORK_AVALIABLE)
11596 /* last chance to get network player actions without main loop delay */
11597 HandleNetworking();
11600 /* game was quit by network peer */
11601 if (game_status != GAME_MODE_PLAYING)
11604 if (!network_player_action_received)
11605 return; /* failed to get network player actions in time */
11607 /* do not yet reset "network_player_action_received" (for tape.pausing) */
11613 /* at this point we know that we really continue executing the game */
11615 network_player_action_received = FALSE;
11617 /* when playing tape, read previously recorded player input from tape data */
11618 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11621 /* TapePlayAction() may return NULL when toggling to "pause before death" */
11626 if (tape.set_centered_player)
11628 game.centered_player_nr_next = tape.centered_player_nr_next;
11629 game.set_centered_player = TRUE;
11632 for (i = 0; i < MAX_PLAYERS; i++)
11634 summarized_player_action |= stored_player[i].action;
11636 if (!network_playing)
11637 stored_player[i].effective_action = stored_player[i].action;
11640 #if defined(NETWORK_AVALIABLE)
11641 if (network_playing)
11642 SendToServer_MovePlayer(summarized_player_action);
11645 if (!options.network && !setup.team_mode)
11646 local_player->effective_action = summarized_player_action;
11648 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
11650 for (i = 0; i < MAX_PLAYERS; i++)
11651 stored_player[i].effective_action =
11652 (i == game.centered_player_nr ? summarized_player_action : 0);
11655 if (recorded_player_action != NULL)
11656 for (i = 0; i < MAX_PLAYERS; i++)
11657 stored_player[i].effective_action = recorded_player_action[i];
11659 for (i = 0; i < MAX_PLAYERS; i++)
11661 tape_action[i] = stored_player[i].effective_action;
11663 /* (this can only happen in the R'n'D game engine) */
11664 if (tape.recording && tape_action[i] && !tape.player_participates[i])
11665 tape.player_participates[i] = TRUE; /* player just appeared from CE */
11668 /* only record actions from input devices, but not programmed actions */
11669 if (tape.recording)
11670 TapeRecordAction(tape_action);
11672 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11674 GameActions_EM_Main();
11682 void GameActions_EM_Main()
11684 byte effective_action[MAX_PLAYERS];
11685 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11688 for (i = 0; i < MAX_PLAYERS; i++)
11689 effective_action[i] = stored_player[i].effective_action;
11691 GameActions_EM(effective_action, warp_mode);
11695 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11698 void GameActions_RND()
11700 int magic_wall_x = 0, magic_wall_y = 0;
11701 int i, x, y, element, graphic;
11703 InitPlayfieldScanModeVars();
11705 #if USE_ONE_MORE_CHANGE_PER_FRAME
11706 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11708 SCAN_PLAYFIELD(x, y)
11710 ChangeCount[x][y] = 0;
11711 ChangeEvent[x][y] = -1;
11716 if (game.set_centered_player)
11718 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11720 /* switching to "all players" only possible if all players fit to screen */
11721 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11723 game.centered_player_nr_next = game.centered_player_nr;
11724 game.set_centered_player = FALSE;
11727 /* do not switch focus to non-existing (or non-active) player */
11728 if (game.centered_player_nr_next >= 0 &&
11729 !stored_player[game.centered_player_nr_next].active)
11731 game.centered_player_nr_next = game.centered_player_nr;
11732 game.set_centered_player = FALSE;
11736 if (game.set_centered_player &&
11737 ScreenMovPos == 0) /* screen currently aligned at tile position */
11741 if (game.centered_player_nr_next == -1)
11743 setScreenCenteredToAllPlayers(&sx, &sy);
11747 sx = stored_player[game.centered_player_nr_next].jx;
11748 sy = stored_player[game.centered_player_nr_next].jy;
11751 game.centered_player_nr = game.centered_player_nr_next;
11752 game.set_centered_player = FALSE;
11754 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11755 DrawGameDoorValues();
11758 for (i = 0; i < MAX_PLAYERS; i++)
11760 int actual_player_action = stored_player[i].effective_action;
11763 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11764 - rnd_equinox_tetrachloride 048
11765 - rnd_equinox_tetrachloride_ii 096
11766 - rnd_emanuel_schmieg 002
11767 - doctor_sloan_ww 001, 020
11769 if (stored_player[i].MovPos == 0)
11770 CheckGravityMovement(&stored_player[i]);
11773 /* overwrite programmed action with tape action */
11774 if (stored_player[i].programmed_action)
11775 actual_player_action = stored_player[i].programmed_action;
11777 PlayerActions(&stored_player[i], actual_player_action);
11779 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11782 ScrollScreen(NULL, SCROLL_GO_ON);
11784 /* for backwards compatibility, the following code emulates a fixed bug that
11785 occured when pushing elements (causing elements that just made their last
11786 pushing step to already (if possible) make their first falling step in the
11787 same game frame, which is bad); this code is also needed to use the famous
11788 "spring push bug" which is used in older levels and might be wanted to be
11789 used also in newer levels, but in this case the buggy pushing code is only
11790 affecting the "spring" element and no other elements */
11792 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11794 for (i = 0; i < MAX_PLAYERS; i++)
11796 struct PlayerInfo *player = &stored_player[i];
11797 int x = player->jx;
11798 int y = player->jy;
11800 if (player->active && player->is_pushing && player->is_moving &&
11802 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11803 Feld[x][y] == EL_SPRING))
11805 ContinueMoving(x, y);
11807 /* continue moving after pushing (this is actually a bug) */
11808 if (!IS_MOVING(x, y))
11809 Stop[x][y] = FALSE;
11815 debug_print_timestamp(0, "start main loop profiling");
11818 SCAN_PLAYFIELD(x, y)
11820 ChangeCount[x][y] = 0;
11821 ChangeEvent[x][y] = -1;
11823 /* this must be handled before main playfield loop */
11824 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11827 if (MovDelay[x][y] <= 0)
11831 #if USE_NEW_SNAP_DELAY
11832 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11835 if (MovDelay[x][y] <= 0)
11838 DrawLevelField(x, y);
11840 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11846 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11848 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11849 printf("GameActions(): This should never happen!\n");
11851 ChangePage[x][y] = -1;
11855 Stop[x][y] = FALSE;
11856 if (WasJustMoving[x][y] > 0)
11857 WasJustMoving[x][y]--;
11858 if (WasJustFalling[x][y] > 0)
11859 WasJustFalling[x][y]--;
11860 if (CheckCollision[x][y] > 0)
11861 CheckCollision[x][y]--;
11862 if (CheckImpact[x][y] > 0)
11863 CheckImpact[x][y]--;
11867 /* reset finished pushing action (not done in ContinueMoving() to allow
11868 continuous pushing animation for elements with zero push delay) */
11869 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11871 ResetGfxAnimation(x, y);
11872 DrawLevelField(x, y);
11876 if (IS_BLOCKED(x, y))
11880 Blocked2Moving(x, y, &oldx, &oldy);
11881 if (!IS_MOVING(oldx, oldy))
11883 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11884 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11885 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11886 printf("GameActions(): This should never happen!\n");
11893 debug_print_timestamp(0, "- time for pre-main loop:");
11896 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
11897 SCAN_PLAYFIELD(x, y)
11899 element = Feld[x][y];
11900 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11905 int element2 = element;
11906 int graphic2 = graphic;
11908 int element2 = Feld[x][y];
11909 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
11911 int last_gfx_frame = GfxFrame[x][y];
11913 if (graphic_info[graphic2].anim_global_sync)
11914 GfxFrame[x][y] = FrameCounter;
11915 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
11916 GfxFrame[x][y] = CustomValue[x][y];
11917 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
11918 GfxFrame[x][y] = element_info[element2].collect_score;
11919 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
11920 GfxFrame[x][y] = ChangeDelay[x][y];
11922 if (redraw && GfxFrame[x][y] != last_gfx_frame)
11923 DrawLevelGraphicAnimation(x, y, graphic2);
11926 ResetGfxFrame(x, y, TRUE);
11930 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11931 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11932 ResetRandomAnimationValue(x, y);
11936 SetRandomAnimationValue(x, y);
11940 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11943 #endif // -------------------- !!! TEST ONLY !!! --------------------
11946 debug_print_timestamp(0, "- time for TEST loop: -->");
11949 SCAN_PLAYFIELD(x, y)
11951 element = Feld[x][y];
11952 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11954 ResetGfxFrame(x, y, TRUE);
11956 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11957 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11958 ResetRandomAnimationValue(x, y);
11960 SetRandomAnimationValue(x, y);
11962 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11964 if (IS_INACTIVE(element))
11966 if (IS_ANIMATED(graphic))
11967 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11972 /* this may take place after moving, so 'element' may have changed */
11973 if (IS_CHANGING(x, y) &&
11974 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11976 int page = element_info[element].event_page_nr[CE_DELAY];
11979 HandleElementChange(x, y, page);
11981 if (CAN_CHANGE(element))
11982 HandleElementChange(x, y, page);
11984 if (HAS_ACTION(element))
11985 ExecuteCustomElementAction(x, y, element, page);
11988 element = Feld[x][y];
11989 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11992 #if 0 // ---------------------------------------------------------------------
11994 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11998 element = Feld[x][y];
11999 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12001 if (IS_ANIMATED(graphic) &&
12002 !IS_MOVING(x, y) &&
12004 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12006 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12007 DrawTwinkleOnField(x, y);
12009 else if (IS_MOVING(x, y))
12010 ContinueMoving(x, y);
12017 case EL_EM_EXIT_OPEN:
12018 case EL_SP_EXIT_OPEN:
12019 case EL_STEEL_EXIT_OPEN:
12020 case EL_EM_STEEL_EXIT_OPEN:
12021 case EL_SP_TERMINAL:
12022 case EL_SP_TERMINAL_ACTIVE:
12023 case EL_EXTRA_TIME:
12024 case EL_SHIELD_NORMAL:
12025 case EL_SHIELD_DEADLY:
12026 if (IS_ANIMATED(graphic))
12027 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12030 case EL_DYNAMITE_ACTIVE:
12031 case EL_EM_DYNAMITE_ACTIVE:
12032 case EL_DYNABOMB_PLAYER_1_ACTIVE:
12033 case EL_DYNABOMB_PLAYER_2_ACTIVE:
12034 case EL_DYNABOMB_PLAYER_3_ACTIVE:
12035 case EL_DYNABOMB_PLAYER_4_ACTIVE:
12036 case EL_SP_DISK_RED_ACTIVE:
12037 CheckDynamite(x, y);
12040 case EL_AMOEBA_GROWING:
12041 AmoebeWaechst(x, y);
12044 case EL_AMOEBA_SHRINKING:
12045 AmoebaDisappearing(x, y);
12048 #if !USE_NEW_AMOEBA_CODE
12049 case EL_AMOEBA_WET:
12050 case EL_AMOEBA_DRY:
12051 case EL_AMOEBA_FULL:
12053 case EL_EMC_DRIPPER:
12054 AmoebeAbleger(x, y);
12058 case EL_GAME_OF_LIFE:
12063 case EL_EXIT_CLOSED:
12067 case EL_EM_EXIT_CLOSED:
12071 case EL_STEEL_EXIT_CLOSED:
12072 CheckExitSteel(x, y);
12075 case EL_EM_STEEL_EXIT_CLOSED:
12076 CheckExitSteelEM(x, y);
12079 case EL_SP_EXIT_CLOSED:
12083 case EL_EXPANDABLE_WALL_GROWING:
12084 case EL_EXPANDABLE_STEELWALL_GROWING:
12085 MauerWaechst(x, y);
12088 case EL_EXPANDABLE_WALL:
12089 case EL_EXPANDABLE_WALL_HORIZONTAL:
12090 case EL_EXPANDABLE_WALL_VERTICAL:
12091 case EL_EXPANDABLE_WALL_ANY:
12092 case EL_BD_EXPANDABLE_WALL:
12093 MauerAbleger(x, y);
12096 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12097 case EL_EXPANDABLE_STEELWALL_VERTICAL:
12098 case EL_EXPANDABLE_STEELWALL_ANY:
12099 MauerAblegerStahl(x, y);
12103 CheckForDragon(x, y);
12109 case EL_ELEMENT_SNAPPING:
12110 case EL_DIAGONAL_SHRINKING:
12111 case EL_DIAGONAL_GROWING:
12114 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12116 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12121 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12122 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12127 #else // ---------------------------------------------------------------------
12129 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12133 element = Feld[x][y];
12134 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12136 if (IS_ANIMATED(graphic) &&
12137 !IS_MOVING(x, y) &&
12139 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12141 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12142 DrawTwinkleOnField(x, y);
12144 else if ((element == EL_ACID ||
12145 element == EL_EXIT_OPEN ||
12146 element == EL_EM_EXIT_OPEN ||
12147 element == EL_SP_EXIT_OPEN ||
12148 element == EL_STEEL_EXIT_OPEN ||
12149 element == EL_EM_STEEL_EXIT_OPEN ||
12150 element == EL_SP_TERMINAL ||
12151 element == EL_SP_TERMINAL_ACTIVE ||
12152 element == EL_EXTRA_TIME ||
12153 element == EL_SHIELD_NORMAL ||
12154 element == EL_SHIELD_DEADLY) &&
12155 IS_ANIMATED(graphic))
12156 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12157 else if (IS_MOVING(x, y))
12158 ContinueMoving(x, y);
12159 else if (IS_ACTIVE_BOMB(element))
12160 CheckDynamite(x, y);
12161 else if (element == EL_AMOEBA_GROWING)
12162 AmoebeWaechst(x, y);
12163 else if (element == EL_AMOEBA_SHRINKING)
12164 AmoebaDisappearing(x, y);
12166 #if !USE_NEW_AMOEBA_CODE
12167 else if (IS_AMOEBALIVE(element))
12168 AmoebeAbleger(x, y);
12171 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12173 else if (element == EL_EXIT_CLOSED)
12175 else if (element == EL_EM_EXIT_CLOSED)
12177 else if (element == EL_STEEL_EXIT_CLOSED)
12178 CheckExitSteel(x, y);
12179 else if (element == EL_EM_STEEL_EXIT_CLOSED)
12180 CheckExitSteelEM(x, y);
12181 else if (element == EL_SP_EXIT_CLOSED)
12183 else if (element == EL_EXPANDABLE_WALL_GROWING ||
12184 element == EL_EXPANDABLE_STEELWALL_GROWING)
12185 MauerWaechst(x, y);
12186 else if (element == EL_EXPANDABLE_WALL ||
12187 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12188 element == EL_EXPANDABLE_WALL_VERTICAL ||
12189 element == EL_EXPANDABLE_WALL_ANY ||
12190 element == EL_BD_EXPANDABLE_WALL)
12191 MauerAbleger(x, y);
12192 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12193 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12194 element == EL_EXPANDABLE_STEELWALL_ANY)
12195 MauerAblegerStahl(x, y);
12196 else if (element == EL_FLAMES)
12197 CheckForDragon(x, y);
12198 else if (element == EL_EXPLOSION)
12199 ; /* drawing of correct explosion animation is handled separately */
12200 else if (element == EL_ELEMENT_SNAPPING ||
12201 element == EL_DIAGONAL_SHRINKING ||
12202 element == EL_DIAGONAL_GROWING)
12204 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12206 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12208 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12209 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12211 #endif // ---------------------------------------------------------------------
12213 if (IS_BELT_ACTIVE(element))
12214 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12216 if (game.magic_wall_active)
12218 int jx = local_player->jx, jy = local_player->jy;
12220 /* play the element sound at the position nearest to the player */
12221 if ((element == EL_MAGIC_WALL_FULL ||
12222 element == EL_MAGIC_WALL_ACTIVE ||
12223 element == EL_MAGIC_WALL_EMPTYING ||
12224 element == EL_BD_MAGIC_WALL_FULL ||
12225 element == EL_BD_MAGIC_WALL_ACTIVE ||
12226 element == EL_BD_MAGIC_WALL_EMPTYING ||
12227 element == EL_DC_MAGIC_WALL_FULL ||
12228 element == EL_DC_MAGIC_WALL_ACTIVE ||
12229 element == EL_DC_MAGIC_WALL_EMPTYING) &&
12230 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12239 debug_print_timestamp(0, "- time for MAIN loop: -->");
12242 #if USE_NEW_AMOEBA_CODE
12243 /* new experimental amoeba growth stuff */
12244 if (!(FrameCounter % 8))
12246 static unsigned long random = 1684108901;
12248 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12250 x = RND(lev_fieldx);
12251 y = RND(lev_fieldy);
12252 element = Feld[x][y];
12254 if (!IS_PLAYER(x,y) &&
12255 (element == EL_EMPTY ||
12256 CAN_GROW_INTO(element) ||
12257 element == EL_QUICKSAND_EMPTY ||
12258 element == EL_QUICKSAND_FAST_EMPTY ||
12259 element == EL_ACID_SPLASH_LEFT ||
12260 element == EL_ACID_SPLASH_RIGHT))
12262 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12263 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12264 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12265 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12266 Feld[x][y] = EL_AMOEBA_DROP;
12269 random = random * 129 + 1;
12275 if (game.explosions_delayed)
12278 game.explosions_delayed = FALSE;
12280 SCAN_PLAYFIELD(x, y)
12282 element = Feld[x][y];
12284 if (ExplodeField[x][y])
12285 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12286 else if (element == EL_EXPLOSION)
12287 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12289 ExplodeField[x][y] = EX_TYPE_NONE;
12292 game.explosions_delayed = TRUE;
12295 if (game.magic_wall_active)
12297 if (!(game.magic_wall_time_left % 4))
12299 int element = Feld[magic_wall_x][magic_wall_y];
12301 if (element == EL_BD_MAGIC_WALL_FULL ||
12302 element == EL_BD_MAGIC_WALL_ACTIVE ||
12303 element == EL_BD_MAGIC_WALL_EMPTYING)
12304 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12305 else if (element == EL_DC_MAGIC_WALL_FULL ||
12306 element == EL_DC_MAGIC_WALL_ACTIVE ||
12307 element == EL_DC_MAGIC_WALL_EMPTYING)
12308 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12310 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12313 if (game.magic_wall_time_left > 0)
12315 game.magic_wall_time_left--;
12317 if (!game.magic_wall_time_left)
12319 SCAN_PLAYFIELD(x, y)
12321 element = Feld[x][y];
12323 if (element == EL_MAGIC_WALL_ACTIVE ||
12324 element == EL_MAGIC_WALL_FULL)
12326 Feld[x][y] = EL_MAGIC_WALL_DEAD;
12327 DrawLevelField(x, y);
12329 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12330 element == EL_BD_MAGIC_WALL_FULL)
12332 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12333 DrawLevelField(x, y);
12335 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12336 element == EL_DC_MAGIC_WALL_FULL)
12338 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12339 DrawLevelField(x, y);
12343 game.magic_wall_active = FALSE;
12348 if (game.light_time_left > 0)
12350 game.light_time_left--;
12352 if (game.light_time_left == 0)
12353 RedrawAllLightSwitchesAndInvisibleElements();
12356 if (game.timegate_time_left > 0)
12358 game.timegate_time_left--;
12360 if (game.timegate_time_left == 0)
12361 CloseAllOpenTimegates();
12364 if (game.lenses_time_left > 0)
12366 game.lenses_time_left--;
12368 if (game.lenses_time_left == 0)
12369 RedrawAllInvisibleElementsForLenses();
12372 if (game.magnify_time_left > 0)
12374 game.magnify_time_left--;
12376 if (game.magnify_time_left == 0)
12377 RedrawAllInvisibleElementsForMagnifier();
12380 for (i = 0; i < MAX_PLAYERS; i++)
12382 struct PlayerInfo *player = &stored_player[i];
12384 if (SHIELD_ON(player))
12386 if (player->shield_deadly_time_left)
12387 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12388 else if (player->shield_normal_time_left)
12389 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12396 PlayAllPlayersSound();
12398 if (options.debug) /* calculate frames per second */
12400 static unsigned long fps_counter = 0;
12401 static int fps_frames = 0;
12402 unsigned long fps_delay_ms = Counter() - fps_counter;
12406 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
12408 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12411 fps_counter = Counter();
12414 redraw_mask |= REDRAW_FPS;
12417 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12419 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12421 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12423 local_player->show_envelope = 0;
12427 debug_print_timestamp(0, "stop main loop profiling ");
12428 printf("----------------------------------------------------------\n");
12431 /* use random number generator in every frame to make it less predictable */
12432 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12436 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12438 int min_x = x, min_y = y, max_x = x, max_y = y;
12441 for (i = 0; i < MAX_PLAYERS; i++)
12443 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12445 if (!stored_player[i].active || &stored_player[i] == player)
12448 min_x = MIN(min_x, jx);
12449 min_y = MIN(min_y, jy);
12450 max_x = MAX(max_x, jx);
12451 max_y = MAX(max_y, jy);
12454 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12457 static boolean AllPlayersInVisibleScreen()
12461 for (i = 0; i < MAX_PLAYERS; i++)
12463 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12465 if (!stored_player[i].active)
12468 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12475 void ScrollLevel(int dx, int dy)
12478 static Bitmap *bitmap_db_field2 = NULL;
12479 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12486 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
12487 /* only horizontal XOR vertical scroll direction allowed */
12488 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
12493 if (bitmap_db_field2 == NULL)
12494 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
12496 /* needed when blitting directly to same bitmap -- should not be needed with
12497 recent SDL libraries, but apparently does not work in 1.2.11 directly */
12498 BlitBitmap(drawto_field, bitmap_db_field2,
12499 FX + TILEX * (dx == -1) - softscroll_offset,
12500 FY + TILEY * (dy == -1) - softscroll_offset,
12501 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12502 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12503 FX + TILEX * (dx == 1) - softscroll_offset,
12504 FY + TILEY * (dy == 1) - softscroll_offset);
12505 BlitBitmap(bitmap_db_field2, drawto_field,
12506 FX + TILEX * (dx == 1) - softscroll_offset,
12507 FY + TILEY * (dy == 1) - softscroll_offset,
12508 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12509 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12510 FX + TILEX * (dx == 1) - softscroll_offset,
12511 FY + TILEY * (dy == 1) - softscroll_offset);
12516 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
12517 int xsize = (BX2 - BX1 + 1);
12518 int ysize = (BY2 - BY1 + 1);
12519 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
12520 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
12521 int step = (start < end ? +1 : -1);
12523 for (i = start; i != end; i += step)
12525 BlitBitmap(drawto_field, drawto_field,
12526 FX + TILEX * (dx != 0 ? i + step : 0),
12527 FY + TILEY * (dy != 0 ? i + step : 0),
12528 TILEX * (dx != 0 ? 1 : xsize),
12529 TILEY * (dy != 0 ? 1 : ysize),
12530 FX + TILEX * (dx != 0 ? i : 0),
12531 FY + TILEY * (dy != 0 ? i : 0));
12536 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12538 BlitBitmap(drawto_field, drawto_field,
12539 FX + TILEX * (dx == -1) - softscroll_offset,
12540 FY + TILEY * (dy == -1) - softscroll_offset,
12541 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12542 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12543 FX + TILEX * (dx == 1) - softscroll_offset,
12544 FY + TILEY * (dy == 1) - softscroll_offset);
12550 x = (dx == 1 ? BX1 : BX2);
12551 for (y = BY1; y <= BY2; y++)
12552 DrawScreenField(x, y);
12557 y = (dy == 1 ? BY1 : BY2);
12558 for (x = BX1; x <= BX2; x++)
12559 DrawScreenField(x, y);
12562 redraw_mask |= REDRAW_FIELD;
12565 static boolean canFallDown(struct PlayerInfo *player)
12567 int jx = player->jx, jy = player->jy;
12569 return (IN_LEV_FIELD(jx, jy + 1) &&
12570 (IS_FREE(jx, jy + 1) ||
12571 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12572 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12573 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12576 static boolean canPassField(int x, int y, int move_dir)
12578 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12579 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12580 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12581 int nextx = x + dx;
12582 int nexty = y + dy;
12583 int element = Feld[x][y];
12585 return (IS_PASSABLE_FROM(element, opposite_dir) &&
12586 !CAN_MOVE(element) &&
12587 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12588 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12589 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12592 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12594 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12595 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12596 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12600 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12601 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12602 (IS_DIGGABLE(Feld[newx][newy]) ||
12603 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12604 canPassField(newx, newy, move_dir)));
12607 static void CheckGravityMovement(struct PlayerInfo *player)
12609 #if USE_PLAYER_GRAVITY
12610 if (player->gravity && !player->programmed_action)
12612 if (game.gravity && !player->programmed_action)
12615 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12616 int move_dir_vertical = player->effective_action & MV_VERTICAL;
12617 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12618 int jx = player->jx, jy = player->jy;
12619 boolean player_is_moving_to_valid_field =
12620 (!player_is_snapping &&
12621 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12622 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12623 boolean player_can_fall_down = canFallDown(player);
12625 if (player_can_fall_down &&
12626 !player_is_moving_to_valid_field)
12627 player->programmed_action = MV_DOWN;
12631 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12633 return CheckGravityMovement(player);
12635 #if USE_PLAYER_GRAVITY
12636 if (player->gravity && !player->programmed_action)
12638 if (game.gravity && !player->programmed_action)
12641 int jx = player->jx, jy = player->jy;
12642 boolean field_under_player_is_free =
12643 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12644 boolean player_is_standing_on_valid_field =
12645 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12646 (IS_WALKABLE(Feld[jx][jy]) &&
12647 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12649 if (field_under_player_is_free && !player_is_standing_on_valid_field)
12650 player->programmed_action = MV_DOWN;
12655 MovePlayerOneStep()
12656 -----------------------------------------------------------------------------
12657 dx, dy: direction (non-diagonal) to try to move the player to
12658 real_dx, real_dy: direction as read from input device (can be diagonal)
12661 boolean MovePlayerOneStep(struct PlayerInfo *player,
12662 int dx, int dy, int real_dx, int real_dy)
12664 int jx = player->jx, jy = player->jy;
12665 int new_jx = jx + dx, new_jy = jy + dy;
12666 #if !USE_FIXED_DONT_RUN_INTO
12670 boolean player_can_move = !player->cannot_move;
12672 if (!player->active || (!dx && !dy))
12673 return MP_NO_ACTION;
12675 player->MovDir = (dx < 0 ? MV_LEFT :
12676 dx > 0 ? MV_RIGHT :
12678 dy > 0 ? MV_DOWN : MV_NONE);
12680 if (!IN_LEV_FIELD(new_jx, new_jy))
12681 return MP_NO_ACTION;
12683 if (!player_can_move)
12685 if (player->MovPos == 0)
12687 player->is_moving = FALSE;
12688 player->is_digging = FALSE;
12689 player->is_collecting = FALSE;
12690 player->is_snapping = FALSE;
12691 player->is_pushing = FALSE;
12696 if (!options.network && game.centered_player_nr == -1 &&
12697 !AllPlayersInSight(player, new_jx, new_jy))
12698 return MP_NO_ACTION;
12700 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
12701 return MP_NO_ACTION;
12704 #if !USE_FIXED_DONT_RUN_INTO
12705 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
12707 /* (moved to DigField()) */
12708 if (player_can_move && DONT_RUN_INTO(element))
12710 if (element == EL_ACID && dx == 0 && dy == 1)
12712 SplashAcid(new_jx, new_jy);
12713 Feld[jx][jy] = EL_PLAYER_1;
12714 InitMovingField(jx, jy, MV_DOWN);
12715 Store[jx][jy] = EL_ACID;
12716 ContinueMoving(jx, jy);
12717 BuryPlayer(player);
12720 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12726 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12727 if (can_move != MP_MOVING)
12730 /* check if DigField() has caused relocation of the player */
12731 if (player->jx != jx || player->jy != jy)
12732 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12734 StorePlayer[jx][jy] = 0;
12735 player->last_jx = jx;
12736 player->last_jy = jy;
12737 player->jx = new_jx;
12738 player->jy = new_jy;
12739 StorePlayer[new_jx][new_jy] = player->element_nr;
12741 if (player->move_delay_value_next != -1)
12743 player->move_delay_value = player->move_delay_value_next;
12744 player->move_delay_value_next = -1;
12748 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12750 player->step_counter++;
12752 PlayerVisit[jx][jy] = FrameCounter;
12754 #if USE_UFAST_PLAYER_EXIT_BUGFIX
12755 player->is_moving = TRUE;
12759 /* should better be called in MovePlayer(), but this breaks some tapes */
12760 ScrollPlayer(player, SCROLL_INIT);
12766 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12768 int jx = player->jx, jy = player->jy;
12769 int old_jx = jx, old_jy = jy;
12770 int moved = MP_NO_ACTION;
12772 if (!player->active)
12777 if (player->MovPos == 0)
12779 player->is_moving = FALSE;
12780 player->is_digging = FALSE;
12781 player->is_collecting = FALSE;
12782 player->is_snapping = FALSE;
12783 player->is_pushing = FALSE;
12789 if (player->move_delay > 0)
12792 player->move_delay = -1; /* set to "uninitialized" value */
12794 /* store if player is automatically moved to next field */
12795 player->is_auto_moving = (player->programmed_action != MV_NONE);
12797 /* remove the last programmed player action */
12798 player->programmed_action = 0;
12800 if (player->MovPos)
12802 /* should only happen if pre-1.2 tape recordings are played */
12803 /* this is only for backward compatibility */
12805 int original_move_delay_value = player->move_delay_value;
12808 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
12812 /* scroll remaining steps with finest movement resolution */
12813 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12815 while (player->MovPos)
12817 ScrollPlayer(player, SCROLL_GO_ON);
12818 ScrollScreen(NULL, SCROLL_GO_ON);
12820 AdvanceFrameAndPlayerCounters(player->index_nr);
12826 player->move_delay_value = original_move_delay_value;
12829 player->is_active = FALSE;
12831 if (player->last_move_dir & MV_HORIZONTAL)
12833 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12834 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12838 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12839 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12842 #if USE_FIXED_BORDER_RUNNING_GFX
12843 if (!moved && !player->is_active)
12845 player->is_moving = FALSE;
12846 player->is_digging = FALSE;
12847 player->is_collecting = FALSE;
12848 player->is_snapping = FALSE;
12849 player->is_pushing = FALSE;
12857 if (moved & MP_MOVING && !ScreenMovPos &&
12858 (player->index_nr == game.centered_player_nr ||
12859 game.centered_player_nr == -1))
12861 if (moved & MP_MOVING && !ScreenMovPos &&
12862 (player == local_player || !options.network))
12865 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12866 int offset = game.scroll_delay_value;
12868 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12870 /* actual player has left the screen -- scroll in that direction */
12871 if (jx != old_jx) /* player has moved horizontally */
12872 scroll_x += (jx - old_jx);
12873 else /* player has moved vertically */
12874 scroll_y += (jy - old_jy);
12878 if (jx != old_jx) /* player has moved horizontally */
12880 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
12881 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12882 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12884 /* don't scroll over playfield boundaries */
12885 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12886 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12888 /* don't scroll more than one field at a time */
12889 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12891 /* don't scroll against the player's moving direction */
12892 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
12893 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12894 scroll_x = old_scroll_x;
12896 else /* player has moved vertically */
12898 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
12899 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12900 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12902 /* don't scroll over playfield boundaries */
12903 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12904 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12906 /* don't scroll more than one field at a time */
12907 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12909 /* don't scroll against the player's moving direction */
12910 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
12911 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12912 scroll_y = old_scroll_y;
12916 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12919 if (!options.network && game.centered_player_nr == -1 &&
12920 !AllPlayersInVisibleScreen())
12922 scroll_x = old_scroll_x;
12923 scroll_y = old_scroll_y;
12927 if (!options.network && !AllPlayersInVisibleScreen())
12929 scroll_x = old_scroll_x;
12930 scroll_y = old_scroll_y;
12935 ScrollScreen(player, SCROLL_INIT);
12936 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12941 player->StepFrame = 0;
12943 if (moved & MP_MOVING)
12945 if (old_jx != jx && old_jy == jy)
12946 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12947 else if (old_jx == jx && old_jy != jy)
12948 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12950 DrawLevelField(jx, jy); /* for "crumbled sand" */
12952 player->last_move_dir = player->MovDir;
12953 player->is_moving = TRUE;
12954 player->is_snapping = FALSE;
12955 player->is_switching = FALSE;
12956 player->is_dropping = FALSE;
12957 player->is_dropping_pressed = FALSE;
12958 player->drop_pressed_delay = 0;
12961 /* should better be called here than above, but this breaks some tapes */
12962 ScrollPlayer(player, SCROLL_INIT);
12967 CheckGravityMovementWhenNotMoving(player);
12969 player->is_moving = FALSE;
12971 /* at this point, the player is allowed to move, but cannot move right now
12972 (e.g. because of something blocking the way) -- ensure that the player
12973 is also allowed to move in the next frame (in old versions before 3.1.1,
12974 the player was forced to wait again for eight frames before next try) */
12976 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12977 player->move_delay = 0; /* allow direct movement in the next frame */
12980 if (player->move_delay == -1) /* not yet initialized by DigField() */
12981 player->move_delay = player->move_delay_value;
12983 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12985 TestIfPlayerTouchesBadThing(jx, jy);
12986 TestIfPlayerTouchesCustomElement(jx, jy);
12989 if (!player->active)
12990 RemovePlayer(player);
12995 void ScrollPlayer(struct PlayerInfo *player, int mode)
12997 int jx = player->jx, jy = player->jy;
12998 int last_jx = player->last_jx, last_jy = player->last_jy;
12999 int move_stepsize = TILEX / player->move_delay_value;
13001 #if USE_NEW_PLAYER_SPEED
13002 if (!player->active)
13005 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
13008 if (!player->active || player->MovPos == 0)
13012 if (mode == SCROLL_INIT)
13014 player->actual_frame_counter = FrameCounter;
13015 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13017 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13018 Feld[last_jx][last_jy] == EL_EMPTY)
13020 int last_field_block_delay = 0; /* start with no blocking at all */
13021 int block_delay_adjustment = player->block_delay_adjustment;
13023 /* if player blocks last field, add delay for exactly one move */
13024 if (player->block_last_field)
13026 last_field_block_delay += player->move_delay_value;
13028 /* when blocking enabled, prevent moving up despite gravity */
13029 #if USE_PLAYER_GRAVITY
13030 if (player->gravity && player->MovDir == MV_UP)
13031 block_delay_adjustment = -1;
13033 if (game.gravity && player->MovDir == MV_UP)
13034 block_delay_adjustment = -1;
13038 /* add block delay adjustment (also possible when not blocking) */
13039 last_field_block_delay += block_delay_adjustment;
13041 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13042 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13045 #if USE_NEW_PLAYER_SPEED
13046 if (player->MovPos != 0) /* player has not yet reached destination */
13052 else if (!FrameReached(&player->actual_frame_counter, 1))
13055 #if USE_NEW_PLAYER_SPEED
13056 if (player->MovPos != 0)
13058 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13059 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13061 /* before DrawPlayer() to draw correct player graphic for this case */
13062 if (player->MovPos == 0)
13063 CheckGravityMovement(player);
13066 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13067 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13069 /* before DrawPlayer() to draw correct player graphic for this case */
13070 if (player->MovPos == 0)
13071 CheckGravityMovement(player);
13074 if (player->MovPos == 0) /* player reached destination field */
13076 if (player->move_delay_reset_counter > 0)
13078 player->move_delay_reset_counter--;
13080 if (player->move_delay_reset_counter == 0)
13082 /* continue with normal speed after quickly moving through gate */
13083 HALVE_PLAYER_SPEED(player);
13085 /* be able to make the next move without delay */
13086 player->move_delay = 0;
13090 player->last_jx = jx;
13091 player->last_jy = jy;
13093 if (Feld[jx][jy] == EL_EXIT_OPEN ||
13094 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13095 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13096 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13097 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13098 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
13100 DrawPlayer(player); /* needed here only to cleanup last field */
13101 RemovePlayer(player);
13103 if (local_player->friends_still_needed == 0 ||
13104 IS_SP_ELEMENT(Feld[jx][jy]))
13105 PlayerWins(player);
13108 /* this breaks one level: "machine", level 000 */
13110 int move_direction = player->MovDir;
13111 int enter_side = MV_DIR_OPPOSITE(move_direction);
13112 int leave_side = move_direction;
13113 int old_jx = last_jx;
13114 int old_jy = last_jy;
13115 int old_element = Feld[old_jx][old_jy];
13116 int new_element = Feld[jx][jy];
13118 if (IS_CUSTOM_ELEMENT(old_element))
13119 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13121 player->index_bit, leave_side);
13123 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13124 CE_PLAYER_LEAVES_X,
13125 player->index_bit, leave_side);
13127 if (IS_CUSTOM_ELEMENT(new_element))
13128 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13129 player->index_bit, enter_side);
13131 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13132 CE_PLAYER_ENTERS_X,
13133 player->index_bit, enter_side);
13135 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13136 CE_MOVE_OF_X, move_direction);
13139 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13141 TestIfPlayerTouchesBadThing(jx, jy);
13142 TestIfPlayerTouchesCustomElement(jx, jy);
13144 /* needed because pushed element has not yet reached its destination,
13145 so it would trigger a change event at its previous field location */
13146 if (!player->is_pushing)
13147 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
13149 if (!player->active)
13150 RemovePlayer(player);
13153 if (!local_player->LevelSolved && level.use_step_counter)
13163 if (TimeLeft <= 10 && setup.time_limit)
13164 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13167 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13169 DisplayGameControlValues();
13171 DrawGameValue_Time(TimeLeft);
13174 if (!TimeLeft && setup.time_limit)
13175 for (i = 0; i < MAX_PLAYERS; i++)
13176 KillPlayer(&stored_player[i]);
13179 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13181 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13183 DisplayGameControlValues();
13186 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13187 DrawGameValue_Time(TimePlayed);
13191 if (tape.single_step && tape.recording && !tape.pausing &&
13192 !player->programmed_action)
13193 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13197 void ScrollScreen(struct PlayerInfo *player, int mode)
13199 static unsigned long screen_frame_counter = 0;
13201 if (mode == SCROLL_INIT)
13203 /* set scrolling step size according to actual player's moving speed */
13204 ScrollStepSize = TILEX / player->move_delay_value;
13206 screen_frame_counter = FrameCounter;
13207 ScreenMovDir = player->MovDir;
13208 ScreenMovPos = player->MovPos;
13209 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13212 else if (!FrameReached(&screen_frame_counter, 1))
13217 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13218 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13219 redraw_mask |= REDRAW_FIELD;
13222 ScreenMovDir = MV_NONE;
13225 void TestIfPlayerTouchesCustomElement(int x, int y)
13227 static int xy[4][2] =
13234 static int trigger_sides[4][2] =
13236 /* center side border side */
13237 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13238 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13239 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13240 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13242 static int touch_dir[4] =
13244 MV_LEFT | MV_RIGHT,
13249 int center_element = Feld[x][y]; /* should always be non-moving! */
13252 for (i = 0; i < NUM_DIRECTIONS; i++)
13254 int xx = x + xy[i][0];
13255 int yy = y + xy[i][1];
13256 int center_side = trigger_sides[i][0];
13257 int border_side = trigger_sides[i][1];
13258 int border_element;
13260 if (!IN_LEV_FIELD(xx, yy))
13263 if (IS_PLAYER(x, y))
13265 struct PlayerInfo *player = PLAYERINFO(x, y);
13267 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13268 border_element = Feld[xx][yy]; /* may be moving! */
13269 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13270 border_element = Feld[xx][yy];
13271 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13272 border_element = MovingOrBlocked2Element(xx, yy);
13274 continue; /* center and border element do not touch */
13276 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13277 player->index_bit, border_side);
13278 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13279 CE_PLAYER_TOUCHES_X,
13280 player->index_bit, border_side);
13282 else if (IS_PLAYER(xx, yy))
13284 struct PlayerInfo *player = PLAYERINFO(xx, yy);
13286 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13288 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13289 continue; /* center and border element do not touch */
13292 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13293 player->index_bit, center_side);
13294 CheckTriggeredElementChangeByPlayer(x, y, center_element,
13295 CE_PLAYER_TOUCHES_X,
13296 player->index_bit, center_side);
13302 #if USE_ELEMENT_TOUCHING_BUGFIX
13304 void TestIfElementTouchesCustomElement(int x, int y)
13306 static int xy[4][2] =
13313 static int trigger_sides[4][2] =
13315 /* center side border side */
13316 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13317 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13318 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13319 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13321 static int touch_dir[4] =
13323 MV_LEFT | MV_RIGHT,
13328 boolean change_center_element = FALSE;
13329 int center_element = Feld[x][y]; /* should always be non-moving! */
13330 int border_element_old[NUM_DIRECTIONS];
13333 for (i = 0; i < NUM_DIRECTIONS; i++)
13335 int xx = x + xy[i][0];
13336 int yy = y + xy[i][1];
13337 int border_element;
13339 border_element_old[i] = -1;
13341 if (!IN_LEV_FIELD(xx, yy))
13344 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13345 border_element = Feld[xx][yy]; /* may be moving! */
13346 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13347 border_element = Feld[xx][yy];
13348 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13349 border_element = MovingOrBlocked2Element(xx, yy);
13351 continue; /* center and border element do not touch */
13353 border_element_old[i] = border_element;
13356 for (i = 0; i < NUM_DIRECTIONS; i++)
13358 int xx = x + xy[i][0];
13359 int yy = y + xy[i][1];
13360 int center_side = trigger_sides[i][0];
13361 int border_element = border_element_old[i];
13363 if (border_element == -1)
13366 /* check for change of border element */
13367 CheckElementChangeBySide(xx, yy, border_element, center_element,
13368 CE_TOUCHING_X, center_side);
13371 for (i = 0; i < NUM_DIRECTIONS; i++)
13373 int border_side = trigger_sides[i][1];
13374 int border_element = border_element_old[i];
13376 if (border_element == -1)
13379 /* check for change of center element (but change it only once) */
13380 if (!change_center_element)
13381 change_center_element =
13382 CheckElementChangeBySide(x, y, center_element, border_element,
13383 CE_TOUCHING_X, border_side);
13389 void TestIfElementTouchesCustomElement_OLD(int x, int y)
13391 static int xy[4][2] =
13398 static int trigger_sides[4][2] =
13400 /* center side border side */
13401 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13402 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13403 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13404 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13406 static int touch_dir[4] =
13408 MV_LEFT | MV_RIGHT,
13413 boolean change_center_element = FALSE;
13414 int center_element = Feld[x][y]; /* should always be non-moving! */
13417 for (i = 0; i < NUM_DIRECTIONS; i++)
13419 int xx = x + xy[i][0];
13420 int yy = y + xy[i][1];
13421 int center_side = trigger_sides[i][0];
13422 int border_side = trigger_sides[i][1];
13423 int border_element;
13425 if (!IN_LEV_FIELD(xx, yy))
13428 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13429 border_element = Feld[xx][yy]; /* may be moving! */
13430 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13431 border_element = Feld[xx][yy];
13432 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13433 border_element = MovingOrBlocked2Element(xx, yy);
13435 continue; /* center and border element do not touch */
13437 /* check for change of center element (but change it only once) */
13438 if (!change_center_element)
13439 change_center_element =
13440 CheckElementChangeBySide(x, y, center_element, border_element,
13441 CE_TOUCHING_X, border_side);
13443 /* check for change of border element */
13444 CheckElementChangeBySide(xx, yy, border_element, center_element,
13445 CE_TOUCHING_X, center_side);
13451 void TestIfElementHitsCustomElement(int x, int y, int direction)
13453 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13454 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
13455 int hitx = x + dx, hity = y + dy;
13456 int hitting_element = Feld[x][y];
13457 int touched_element;
13459 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13462 touched_element = (IN_LEV_FIELD(hitx, hity) ?
13463 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13465 if (IN_LEV_FIELD(hitx, hity))
13467 int opposite_direction = MV_DIR_OPPOSITE(direction);
13468 int hitting_side = direction;
13469 int touched_side = opposite_direction;
13470 boolean object_hit = (!IS_MOVING(hitx, hity) ||
13471 MovDir[hitx][hity] != direction ||
13472 ABS(MovPos[hitx][hity]) <= TILEY / 2);
13478 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13479 CE_HITTING_X, touched_side);
13481 CheckElementChangeBySide(hitx, hity, touched_element,
13482 hitting_element, CE_HIT_BY_X, hitting_side);
13484 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13485 CE_HIT_BY_SOMETHING, opposite_direction);
13489 /* "hitting something" is also true when hitting the playfield border */
13490 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13491 CE_HITTING_SOMETHING, direction);
13495 void TestIfElementSmashesCustomElement(int x, int y, int direction)
13497 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13498 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
13499 int hitx = x + dx, hity = y + dy;
13500 int hitting_element = Feld[x][y];
13501 int touched_element;
13503 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
13504 !IS_FREE(hitx, hity) &&
13505 (!IS_MOVING(hitx, hity) ||
13506 MovDir[hitx][hity] != direction ||
13507 ABS(MovPos[hitx][hity]) <= TILEY / 2));
13510 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13514 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
13518 touched_element = (IN_LEV_FIELD(hitx, hity) ?
13519 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13521 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13522 EP_CAN_SMASH_EVERYTHING, direction);
13524 if (IN_LEV_FIELD(hitx, hity))
13526 int opposite_direction = MV_DIR_OPPOSITE(direction);
13527 int hitting_side = direction;
13528 int touched_side = opposite_direction;
13530 int touched_element = MovingOrBlocked2Element(hitx, hity);
13533 boolean object_hit = (!IS_MOVING(hitx, hity) ||
13534 MovDir[hitx][hity] != direction ||
13535 ABS(MovPos[hitx][hity]) <= TILEY / 2);
13544 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13545 CE_SMASHED_BY_SOMETHING, opposite_direction);
13547 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13548 CE_OTHER_IS_SMASHING, touched_side);
13550 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13551 CE_OTHER_GETS_SMASHED, hitting_side);
13557 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13559 int i, kill_x = -1, kill_y = -1;
13561 int bad_element = -1;
13562 static int test_xy[4][2] =
13569 static int test_dir[4] =
13577 for (i = 0; i < NUM_DIRECTIONS; i++)
13579 int test_x, test_y, test_move_dir, test_element;
13581 test_x = good_x + test_xy[i][0];
13582 test_y = good_y + test_xy[i][1];
13584 if (!IN_LEV_FIELD(test_x, test_y))
13588 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13590 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13592 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13593 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13595 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13596 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
13600 bad_element = test_element;
13606 if (kill_x != -1 || kill_y != -1)
13608 if (IS_PLAYER(good_x, good_y))
13610 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13612 if (player->shield_deadly_time_left > 0 &&
13613 !IS_INDESTRUCTIBLE(bad_element))
13614 Bang(kill_x, kill_y);
13615 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13616 KillPlayer(player);
13619 Bang(good_x, good_y);
13623 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13625 int i, kill_x = -1, kill_y = -1;
13626 int bad_element = Feld[bad_x][bad_y];
13627 static int test_xy[4][2] =
13634 static int touch_dir[4] =
13636 MV_LEFT | MV_RIGHT,
13641 static int test_dir[4] =
13649 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
13652 for (i = 0; i < NUM_DIRECTIONS; i++)
13654 int test_x, test_y, test_move_dir, test_element;
13656 test_x = bad_x + test_xy[i][0];
13657 test_y = bad_y + test_xy[i][1];
13658 if (!IN_LEV_FIELD(test_x, test_y))
13662 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13664 test_element = Feld[test_x][test_y];
13666 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13667 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13669 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
13670 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
13672 /* good thing is player or penguin that does not move away */
13673 if (IS_PLAYER(test_x, test_y))
13675 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13677 if (bad_element == EL_ROBOT && player->is_moving)
13678 continue; /* robot does not kill player if he is moving */
13680 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13682 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13683 continue; /* center and border element do not touch */
13690 else if (test_element == EL_PENGUIN)
13699 if (kill_x != -1 || kill_y != -1)
13701 if (IS_PLAYER(kill_x, kill_y))
13703 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13705 if (player->shield_deadly_time_left > 0 &&
13706 !IS_INDESTRUCTIBLE(bad_element))
13707 Bang(bad_x, bad_y);
13708 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13709 KillPlayer(player);
13712 Bang(kill_x, kill_y);
13716 void TestIfPlayerTouchesBadThing(int x, int y)
13718 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13721 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13723 TestIfGoodThingHitsBadThing(x, y, move_dir);
13726 void TestIfBadThingTouchesPlayer(int x, int y)
13728 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13731 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13733 TestIfBadThingHitsGoodThing(x, y, move_dir);
13736 void TestIfFriendTouchesBadThing(int x, int y)
13738 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13741 void TestIfBadThingTouchesFriend(int x, int y)
13743 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13746 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13748 int i, kill_x = bad_x, kill_y = bad_y;
13749 static int xy[4][2] =
13757 for (i = 0; i < NUM_DIRECTIONS; i++)
13761 x = bad_x + xy[i][0];
13762 y = bad_y + xy[i][1];
13763 if (!IN_LEV_FIELD(x, y))
13766 element = Feld[x][y];
13767 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13768 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13776 if (kill_x != bad_x || kill_y != bad_y)
13777 Bang(bad_x, bad_y);
13780 void KillPlayer(struct PlayerInfo *player)
13782 int jx = player->jx, jy = player->jy;
13784 if (!player->active)
13787 /* the following code was introduced to prevent an infinite loop when calling
13789 -> CheckTriggeredElementChangeExt()
13790 -> ExecuteCustomElementAction()
13792 -> (infinitely repeating the above sequence of function calls)
13793 which occurs when killing the player while having a CE with the setting
13794 "kill player X when explosion of <player X>"; the solution using a new
13795 field "player->killed" was chosen for backwards compatibility, although
13796 clever use of the fields "player->active" etc. would probably also work */
13798 if (player->killed)
13802 player->killed = TRUE;
13804 /* remove accessible field at the player's position */
13805 Feld[jx][jy] = EL_EMPTY;
13807 /* deactivate shield (else Bang()/Explode() would not work right) */
13808 player->shield_normal_time_left = 0;
13809 player->shield_deadly_time_left = 0;
13812 BuryPlayer(player);
13815 static void KillPlayerUnlessEnemyProtected(int x, int y)
13817 if (!PLAYER_ENEMY_PROTECTED(x, y))
13818 KillPlayer(PLAYERINFO(x, y));
13821 static void KillPlayerUnlessExplosionProtected(int x, int y)
13823 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13824 KillPlayer(PLAYERINFO(x, y));
13827 void BuryPlayer(struct PlayerInfo *player)
13829 int jx = player->jx, jy = player->jy;
13831 if (!player->active)
13834 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13835 PlayLevelSound(jx, jy, SND_GAME_LOSING);
13837 player->GameOver = TRUE;
13838 RemovePlayer(player);
13841 void RemovePlayer(struct PlayerInfo *player)
13843 int jx = player->jx, jy = player->jy;
13844 int i, found = FALSE;
13846 player->present = FALSE;
13847 player->active = FALSE;
13849 if (!ExplodeField[jx][jy])
13850 StorePlayer[jx][jy] = 0;
13852 if (player->is_moving)
13853 DrawLevelField(player->last_jx, player->last_jy);
13855 for (i = 0; i < MAX_PLAYERS; i++)
13856 if (stored_player[i].active)
13860 AllPlayersGone = TRUE;
13866 #if USE_NEW_SNAP_DELAY
13867 static void setFieldForSnapping(int x, int y, int element, int direction)
13869 struct ElementInfo *ei = &element_info[element];
13870 int direction_bit = MV_DIR_TO_BIT(direction);
13871 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13872 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13873 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13875 Feld[x][y] = EL_ELEMENT_SNAPPING;
13876 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13878 ResetGfxAnimation(x, y);
13880 GfxElement[x][y] = element;
13881 GfxAction[x][y] = action;
13882 GfxDir[x][y] = direction;
13883 GfxFrame[x][y] = -1;
13888 =============================================================================
13889 checkDiagonalPushing()
13890 -----------------------------------------------------------------------------
13891 check if diagonal input device direction results in pushing of object
13892 (by checking if the alternative direction is walkable, diggable, ...)
13893 =============================================================================
13896 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13897 int x, int y, int real_dx, int real_dy)
13899 int jx, jy, dx, dy, xx, yy;
13901 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
13904 /* diagonal direction: check alternative direction */
13909 xx = jx + (dx == 0 ? real_dx : 0);
13910 yy = jy + (dy == 0 ? real_dy : 0);
13912 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13916 =============================================================================
13918 -----------------------------------------------------------------------------
13919 x, y: field next to player (non-diagonal) to try to dig to
13920 real_dx, real_dy: direction as read from input device (can be diagonal)
13921 =============================================================================
13924 int DigField(struct PlayerInfo *player,
13925 int oldx, int oldy, int x, int y,
13926 int real_dx, int real_dy, int mode)
13928 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13929 boolean player_was_pushing = player->is_pushing;
13930 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13931 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13932 int jx = oldx, jy = oldy;
13933 int dx = x - jx, dy = y - jy;
13934 int nextx = x + dx, nexty = y + dy;
13935 int move_direction = (dx == -1 ? MV_LEFT :
13936 dx == +1 ? MV_RIGHT :
13938 dy == +1 ? MV_DOWN : MV_NONE);
13939 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13940 int dig_side = MV_DIR_OPPOSITE(move_direction);
13941 int old_element = Feld[jx][jy];
13942 #if USE_FIXED_DONT_RUN_INTO
13943 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13949 if (is_player) /* function can also be called by EL_PENGUIN */
13951 if (player->MovPos == 0)
13953 player->is_digging = FALSE;
13954 player->is_collecting = FALSE;
13957 if (player->MovPos == 0) /* last pushing move finished */
13958 player->is_pushing = FALSE;
13960 if (mode == DF_NO_PUSH) /* player just stopped pushing */
13962 player->is_switching = FALSE;
13963 player->push_delay = -1;
13965 return MP_NO_ACTION;
13969 #if !USE_FIXED_DONT_RUN_INTO
13970 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13971 return MP_NO_ACTION;
13974 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13975 old_element = Back[jx][jy];
13977 /* in case of element dropped at player position, check background */
13978 else if (Back[jx][jy] != EL_EMPTY &&
13979 game.engine_version >= VERSION_IDENT(2,2,0,0))
13980 old_element = Back[jx][jy];
13982 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13983 return MP_NO_ACTION; /* field has no opening in this direction */
13985 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13986 return MP_NO_ACTION; /* field has no opening in this direction */
13988 #if USE_FIXED_DONT_RUN_INTO
13989 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13993 Feld[jx][jy] = player->artwork_element;
13994 InitMovingField(jx, jy, MV_DOWN);
13995 Store[jx][jy] = EL_ACID;
13996 ContinueMoving(jx, jy);
13997 BuryPlayer(player);
13999 return MP_DONT_RUN_INTO;
14003 #if USE_FIXED_DONT_RUN_INTO
14004 if (player_can_move && DONT_RUN_INTO(element))
14006 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14008 return MP_DONT_RUN_INTO;
14012 #if USE_FIXED_DONT_RUN_INTO
14013 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14014 return MP_NO_ACTION;
14017 #if !USE_FIXED_DONT_RUN_INTO
14018 element = Feld[x][y];
14021 collect_count = element_info[element].collect_count_initial;
14023 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
14024 return MP_NO_ACTION;
14026 if (game.engine_version < VERSION_IDENT(2,2,0,0))
14027 player_can_move = player_can_move_or_snap;
14029 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14030 game.engine_version >= VERSION_IDENT(2,2,0,0))
14032 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14033 player->index_bit, dig_side);
14034 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14035 player->index_bit, dig_side);
14037 if (element == EL_DC_LANDMINE)
14040 if (Feld[x][y] != element) /* field changed by snapping */
14043 return MP_NO_ACTION;
14046 #if USE_PLAYER_GRAVITY
14047 if (player->gravity && is_player && !player->is_auto_moving &&
14048 canFallDown(player) && move_direction != MV_DOWN &&
14049 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14050 return MP_NO_ACTION; /* player cannot walk here due to gravity */
14052 if (game.gravity && is_player && !player->is_auto_moving &&
14053 canFallDown(player) && move_direction != MV_DOWN &&
14054 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14055 return MP_NO_ACTION; /* player cannot walk here due to gravity */
14058 if (player_can_move &&
14059 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14061 int sound_element = SND_ELEMENT(element);
14062 int sound_action = ACTION_WALKING;
14064 if (IS_RND_GATE(element))
14066 if (!player->key[RND_GATE_NR(element)])
14067 return MP_NO_ACTION;
14069 else if (IS_RND_GATE_GRAY(element))
14071 if (!player->key[RND_GATE_GRAY_NR(element)])
14072 return MP_NO_ACTION;
14074 else if (IS_RND_GATE_GRAY_ACTIVE(element))
14076 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14077 return MP_NO_ACTION;
14079 else if (element == EL_EXIT_OPEN ||
14080 element == EL_EM_EXIT_OPEN ||
14081 element == EL_STEEL_EXIT_OPEN ||
14082 element == EL_EM_STEEL_EXIT_OPEN ||
14083 element == EL_SP_EXIT_OPEN ||
14084 element == EL_SP_EXIT_OPENING)
14086 sound_action = ACTION_PASSING; /* player is passing exit */
14088 else if (element == EL_EMPTY)
14090 sound_action = ACTION_MOVING; /* nothing to walk on */
14093 /* play sound from background or player, whatever is available */
14094 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14095 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14097 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14099 else if (player_can_move &&
14100 IS_PASSABLE(element) && canPassField(x, y, move_direction))
14102 if (!ACCESS_FROM(element, opposite_direction))
14103 return MP_NO_ACTION; /* field not accessible from this direction */
14105 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
14106 return MP_NO_ACTION;
14108 if (IS_EM_GATE(element))
14110 if (!player->key[EM_GATE_NR(element)])
14111 return MP_NO_ACTION;
14113 else if (IS_EM_GATE_GRAY(element))
14115 if (!player->key[EM_GATE_GRAY_NR(element)])
14116 return MP_NO_ACTION;
14118 else if (IS_EM_GATE_GRAY_ACTIVE(element))
14120 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14121 return MP_NO_ACTION;
14123 else if (IS_EMC_GATE(element))
14125 if (!player->key[EMC_GATE_NR(element)])
14126 return MP_NO_ACTION;
14128 else if (IS_EMC_GATE_GRAY(element))
14130 if (!player->key[EMC_GATE_GRAY_NR(element)])
14131 return MP_NO_ACTION;
14133 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14135 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14136 return MP_NO_ACTION;
14138 else if (element == EL_DC_GATE_WHITE ||
14139 element == EL_DC_GATE_WHITE_GRAY ||
14140 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14142 if (player->num_white_keys == 0)
14143 return MP_NO_ACTION;
14145 player->num_white_keys--;
14147 else if (IS_SP_PORT(element))
14149 if (element == EL_SP_GRAVITY_PORT_LEFT ||
14150 element == EL_SP_GRAVITY_PORT_RIGHT ||
14151 element == EL_SP_GRAVITY_PORT_UP ||
14152 element == EL_SP_GRAVITY_PORT_DOWN)
14153 #if USE_PLAYER_GRAVITY
14154 player->gravity = !player->gravity;
14156 game.gravity = !game.gravity;
14158 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14159 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14160 element == EL_SP_GRAVITY_ON_PORT_UP ||
14161 element == EL_SP_GRAVITY_ON_PORT_DOWN)
14162 #if USE_PLAYER_GRAVITY
14163 player->gravity = TRUE;
14165 game.gravity = TRUE;
14167 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14168 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14169 element == EL_SP_GRAVITY_OFF_PORT_UP ||
14170 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14171 #if USE_PLAYER_GRAVITY
14172 player->gravity = FALSE;
14174 game.gravity = FALSE;
14178 /* automatically move to the next field with double speed */
14179 player->programmed_action = move_direction;
14181 if (player->move_delay_reset_counter == 0)
14183 player->move_delay_reset_counter = 2; /* two double speed steps */
14185 DOUBLE_PLAYER_SPEED(player);
14188 PlayLevelSoundAction(x, y, ACTION_PASSING);
14190 else if (player_can_move_or_snap && IS_DIGGABLE(element))
14194 if (mode != DF_SNAP)
14196 GfxElement[x][y] = GFX_ELEMENT(element);
14197 player->is_digging = TRUE;
14200 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14202 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14203 player->index_bit, dig_side);
14205 if (mode == DF_SNAP)
14207 #if USE_NEW_SNAP_DELAY
14208 if (level.block_snap_field)
14209 setFieldForSnapping(x, y, element, move_direction);
14211 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14213 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14216 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14217 player->index_bit, dig_side);
14220 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14224 if (is_player && mode != DF_SNAP)
14226 GfxElement[x][y] = element;
14227 player->is_collecting = TRUE;
14230 if (element == EL_SPEED_PILL)
14232 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14234 else if (element == EL_EXTRA_TIME && level.time > 0)
14236 TimeLeft += level.extra_time;
14239 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14241 DisplayGameControlValues();
14243 DrawGameValue_Time(TimeLeft);
14246 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14248 player->shield_normal_time_left += level.shield_normal_time;
14249 if (element == EL_SHIELD_DEADLY)
14250 player->shield_deadly_time_left += level.shield_deadly_time;
14252 else if (element == EL_DYNAMITE ||
14253 element == EL_EM_DYNAMITE ||
14254 element == EL_SP_DISK_RED)
14256 if (player->inventory_size < MAX_INVENTORY_SIZE)
14257 player->inventory_element[player->inventory_size++] = element;
14259 DrawGameDoorValues();
14261 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14263 player->dynabomb_count++;
14264 player->dynabombs_left++;
14266 else if (element == EL_DYNABOMB_INCREASE_SIZE)
14268 player->dynabomb_size++;
14270 else if (element == EL_DYNABOMB_INCREASE_POWER)
14272 player->dynabomb_xl = TRUE;
14274 else if (IS_KEY(element))
14276 player->key[KEY_NR(element)] = TRUE;
14278 DrawGameDoorValues();
14280 else if (element == EL_DC_KEY_WHITE)
14282 player->num_white_keys++;
14284 /* display white keys? */
14285 /* DrawGameDoorValues(); */
14287 else if (IS_ENVELOPE(element))
14289 player->show_envelope = element;
14291 else if (element == EL_EMC_LENSES)
14293 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14295 RedrawAllInvisibleElementsForLenses();
14297 else if (element == EL_EMC_MAGNIFIER)
14299 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14301 RedrawAllInvisibleElementsForMagnifier();
14303 else if (IS_DROPPABLE(element) ||
14304 IS_THROWABLE(element)) /* can be collected and dropped */
14308 if (collect_count == 0)
14309 player->inventory_infinite_element = element;
14311 for (i = 0; i < collect_count; i++)
14312 if (player->inventory_size < MAX_INVENTORY_SIZE)
14313 player->inventory_element[player->inventory_size++] = element;
14315 DrawGameDoorValues();
14317 else if (collect_count > 0)
14319 local_player->gems_still_needed -= collect_count;
14320 if (local_player->gems_still_needed < 0)
14321 local_player->gems_still_needed = 0;
14324 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
14326 DisplayGameControlValues();
14328 DrawGameValue_Emeralds(local_player->gems_still_needed);
14332 RaiseScoreElement(element);
14333 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14336 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14337 player->index_bit, dig_side);
14339 if (mode == DF_SNAP)
14341 #if USE_NEW_SNAP_DELAY
14342 if (level.block_snap_field)
14343 setFieldForSnapping(x, y, element, move_direction);
14345 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14347 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14350 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14351 player->index_bit, dig_side);
14354 else if (player_can_move_or_snap && IS_PUSHABLE(element))
14356 if (mode == DF_SNAP && element != EL_BD_ROCK)
14357 return MP_NO_ACTION;
14359 if (CAN_FALL(element) && dy)
14360 return MP_NO_ACTION;
14362 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14363 !(element == EL_SPRING && level.use_spring_bug))
14364 return MP_NO_ACTION;
14366 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14367 ((move_direction & MV_VERTICAL &&
14368 ((element_info[element].move_pattern & MV_LEFT &&
14369 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14370 (element_info[element].move_pattern & MV_RIGHT &&
14371 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14372 (move_direction & MV_HORIZONTAL &&
14373 ((element_info[element].move_pattern & MV_UP &&
14374 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14375 (element_info[element].move_pattern & MV_DOWN &&
14376 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14377 return MP_NO_ACTION;
14379 /* do not push elements already moving away faster than player */
14380 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14381 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14382 return MP_NO_ACTION;
14384 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14386 if (player->push_delay_value == -1 || !player_was_pushing)
14387 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14389 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14391 if (player->push_delay_value == -1)
14392 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14394 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14396 if (!player->is_pushing)
14397 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14400 player->is_pushing = TRUE;
14401 player->is_active = TRUE;
14403 if (!(IN_LEV_FIELD(nextx, nexty) &&
14404 (IS_FREE(nextx, nexty) ||
14405 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
14406 IS_SB_ELEMENT(element)))))
14407 return MP_NO_ACTION;
14409 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14410 return MP_NO_ACTION;
14412 if (player->push_delay == -1) /* new pushing; restart delay */
14413 player->push_delay = 0;
14415 if (player->push_delay < player->push_delay_value &&
14416 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14417 element != EL_SPRING && element != EL_BALLOON)
14419 /* make sure that there is no move delay before next try to push */
14420 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14421 player->move_delay = 0;
14423 return MP_NO_ACTION;
14426 if (IS_SB_ELEMENT(element))
14428 if (element == EL_SOKOBAN_FIELD_FULL)
14430 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14431 local_player->sokobanfields_still_needed++;
14434 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14436 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14437 local_player->sokobanfields_still_needed--;
14440 Feld[x][y] = EL_SOKOBAN_OBJECT;
14442 if (Back[x][y] == Back[nextx][nexty])
14443 PlayLevelSoundAction(x, y, ACTION_PUSHING);
14444 else if (Back[x][y] != 0)
14445 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14448 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14451 if (local_player->sokobanfields_still_needed == 0 &&
14452 game.emulation == EMU_SOKOBAN)
14454 PlayerWins(player);
14456 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14460 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14462 InitMovingField(x, y, move_direction);
14463 GfxAction[x][y] = ACTION_PUSHING;
14465 if (mode == DF_SNAP)
14466 ContinueMoving(x, y);
14468 MovPos[x][y] = (dx != 0 ? dx : dy);
14470 Pushed[x][y] = TRUE;
14471 Pushed[nextx][nexty] = TRUE;
14473 if (game.engine_version < VERSION_IDENT(2,2,0,7))
14474 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14476 player->push_delay_value = -1; /* get new value later */
14478 /* check for element change _after_ element has been pushed */
14479 if (game.use_change_when_pushing_bug)
14481 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14482 player->index_bit, dig_side);
14483 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14484 player->index_bit, dig_side);
14487 else if (IS_SWITCHABLE(element))
14489 if (PLAYER_SWITCHING(player, x, y))
14491 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14492 player->index_bit, dig_side);
14497 player->is_switching = TRUE;
14498 player->switch_x = x;
14499 player->switch_y = y;
14501 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14503 if (element == EL_ROBOT_WHEEL)
14505 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14509 game.robot_wheel_active = TRUE;
14511 DrawLevelField(x, y);
14513 else if (element == EL_SP_TERMINAL)
14517 SCAN_PLAYFIELD(xx, yy)
14519 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14521 else if (Feld[xx][yy] == EL_SP_TERMINAL)
14522 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14525 else if (IS_BELT_SWITCH(element))
14527 ToggleBeltSwitch(x, y);
14529 else if (element == EL_SWITCHGATE_SWITCH_UP ||
14530 element == EL_SWITCHGATE_SWITCH_DOWN ||
14531 element == EL_DC_SWITCHGATE_SWITCH_UP ||
14532 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14534 ToggleSwitchgateSwitch(x, y);
14536 else if (element == EL_LIGHT_SWITCH ||
14537 element == EL_LIGHT_SWITCH_ACTIVE)
14539 ToggleLightSwitch(x, y);
14541 else if (element == EL_TIMEGATE_SWITCH ||
14542 element == EL_DC_TIMEGATE_SWITCH)
14544 ActivateTimegateSwitch(x, y);
14546 else if (element == EL_BALLOON_SWITCH_LEFT ||
14547 element == EL_BALLOON_SWITCH_RIGHT ||
14548 element == EL_BALLOON_SWITCH_UP ||
14549 element == EL_BALLOON_SWITCH_DOWN ||
14550 element == EL_BALLOON_SWITCH_NONE ||
14551 element == EL_BALLOON_SWITCH_ANY)
14553 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
14554 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14555 element == EL_BALLOON_SWITCH_UP ? MV_UP :
14556 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
14557 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
14560 else if (element == EL_LAMP)
14562 Feld[x][y] = EL_LAMP_ACTIVE;
14563 local_player->lights_still_needed--;
14565 ResetGfxAnimation(x, y);
14566 DrawLevelField(x, y);
14568 else if (element == EL_TIME_ORB_FULL)
14570 Feld[x][y] = EL_TIME_ORB_EMPTY;
14572 if (level.time > 0 || level.use_time_orb_bug)
14574 TimeLeft += level.time_orb_time;
14577 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14579 DisplayGameControlValues();
14581 DrawGameValue_Time(TimeLeft);
14585 ResetGfxAnimation(x, y);
14586 DrawLevelField(x, y);
14588 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14589 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14593 game.ball_state = !game.ball_state;
14595 SCAN_PLAYFIELD(xx, yy)
14597 int e = Feld[xx][yy];
14599 if (game.ball_state)
14601 if (e == EL_EMC_MAGIC_BALL)
14602 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14603 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14604 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14608 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14609 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14610 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14611 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14616 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14617 player->index_bit, dig_side);
14619 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14620 player->index_bit, dig_side);
14622 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14623 player->index_bit, dig_side);
14629 if (!PLAYER_SWITCHING(player, x, y))
14631 player->is_switching = TRUE;
14632 player->switch_x = x;
14633 player->switch_y = y;
14635 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14636 player->index_bit, dig_side);
14637 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14638 player->index_bit, dig_side);
14640 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14641 player->index_bit, dig_side);
14642 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14643 player->index_bit, dig_side);
14646 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14647 player->index_bit, dig_side);
14648 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14649 player->index_bit, dig_side);
14651 return MP_NO_ACTION;
14654 player->push_delay = -1;
14656 if (is_player) /* function can also be called by EL_PENGUIN */
14658 if (Feld[x][y] != element) /* really digged/collected something */
14660 player->is_collecting = !player->is_digging;
14661 player->is_active = TRUE;
14668 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14670 int jx = player->jx, jy = player->jy;
14671 int x = jx + dx, y = jy + dy;
14672 int snap_direction = (dx == -1 ? MV_LEFT :
14673 dx == +1 ? MV_RIGHT :
14675 dy == +1 ? MV_DOWN : MV_NONE);
14676 boolean can_continue_snapping = (level.continuous_snapping &&
14677 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14679 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14682 if (!player->active || !IN_LEV_FIELD(x, y))
14690 if (player->MovPos == 0)
14691 player->is_pushing = FALSE;
14693 player->is_snapping = FALSE;
14695 if (player->MovPos == 0)
14697 player->is_moving = FALSE;
14698 player->is_digging = FALSE;
14699 player->is_collecting = FALSE;
14705 #if USE_NEW_CONTINUOUS_SNAPPING
14706 /* prevent snapping with already pressed snap key when not allowed */
14707 if (player->is_snapping && !can_continue_snapping)
14710 if (player->is_snapping)
14714 player->MovDir = snap_direction;
14716 if (player->MovPos == 0)
14718 player->is_moving = FALSE;
14719 player->is_digging = FALSE;
14720 player->is_collecting = FALSE;
14723 player->is_dropping = FALSE;
14724 player->is_dropping_pressed = FALSE;
14725 player->drop_pressed_delay = 0;
14727 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14730 player->is_snapping = TRUE;
14731 player->is_active = TRUE;
14733 if (player->MovPos == 0)
14735 player->is_moving = FALSE;
14736 player->is_digging = FALSE;
14737 player->is_collecting = FALSE;
14740 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
14741 DrawLevelField(player->last_jx, player->last_jy);
14743 DrawLevelField(x, y);
14748 boolean DropElement(struct PlayerInfo *player)
14750 int old_element, new_element;
14751 int dropx = player->jx, dropy = player->jy;
14752 int drop_direction = player->MovDir;
14753 int drop_side = drop_direction;
14755 int drop_element = get_next_dropped_element(player);
14757 int drop_element = (player->inventory_size > 0 ?
14758 player->inventory_element[player->inventory_size - 1] :
14759 player->inventory_infinite_element != EL_UNDEFINED ?
14760 player->inventory_infinite_element :
14761 player->dynabombs_left > 0 ?
14762 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
14766 player->is_dropping_pressed = TRUE;
14768 /* do not drop an element on top of another element; when holding drop key
14769 pressed without moving, dropped element must move away before the next
14770 element can be dropped (this is especially important if the next element
14771 is dynamite, which can be placed on background for historical reasons) */
14772 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14775 if (IS_THROWABLE(drop_element))
14777 dropx += GET_DX_FROM_DIR(drop_direction);
14778 dropy += GET_DY_FROM_DIR(drop_direction);
14780 if (!IN_LEV_FIELD(dropx, dropy))
14784 old_element = Feld[dropx][dropy]; /* old element at dropping position */
14785 new_element = drop_element; /* default: no change when dropping */
14787 /* check if player is active, not moving and ready to drop */
14788 if (!player->active || player->MovPos || player->drop_delay > 0)
14791 /* check if player has anything that can be dropped */
14792 if (new_element == EL_UNDEFINED)
14795 /* check if drop key was pressed long enough for EM style dynamite */
14796 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14799 /* check if anything can be dropped at the current position */
14800 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14803 /* collected custom elements can only be dropped on empty fields */
14804 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14807 if (old_element != EL_EMPTY)
14808 Back[dropx][dropy] = old_element; /* store old element on this field */
14810 ResetGfxAnimation(dropx, dropy);
14811 ResetRandomAnimationValue(dropx, dropy);
14813 if (player->inventory_size > 0 ||
14814 player->inventory_infinite_element != EL_UNDEFINED)
14816 if (player->inventory_size > 0)
14818 player->inventory_size--;
14820 DrawGameDoorValues();
14822 if (new_element == EL_DYNAMITE)
14823 new_element = EL_DYNAMITE_ACTIVE;
14824 else if (new_element == EL_EM_DYNAMITE)
14825 new_element = EL_EM_DYNAMITE_ACTIVE;
14826 else if (new_element == EL_SP_DISK_RED)
14827 new_element = EL_SP_DISK_RED_ACTIVE;
14830 Feld[dropx][dropy] = new_element;
14832 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14833 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14834 el2img(Feld[dropx][dropy]), 0);
14836 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14838 /* needed if previous element just changed to "empty" in the last frame */
14839 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14841 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14842 player->index_bit, drop_side);
14843 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14845 player->index_bit, drop_side);
14847 TestIfElementTouchesCustomElement(dropx, dropy);
14849 else /* player is dropping a dyna bomb */
14851 player->dynabombs_left--;
14853 Feld[dropx][dropy] = new_element;
14855 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14856 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14857 el2img(Feld[dropx][dropy]), 0);
14859 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14862 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14863 InitField_WithBug1(dropx, dropy, FALSE);
14865 new_element = Feld[dropx][dropy]; /* element might have changed */
14867 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14868 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14870 int move_direction, nextx, nexty;
14872 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14873 MovDir[dropx][dropy] = drop_direction;
14875 move_direction = MovDir[dropx][dropy];
14876 nextx = dropx + GET_DX_FROM_DIR(move_direction);
14877 nexty = dropy + GET_DY_FROM_DIR(move_direction);
14879 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14881 #if USE_FIX_IMPACT_COLLISION
14882 /* do not cause impact style collision by dropping elements that can fall */
14883 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14885 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14889 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14890 player->is_dropping = TRUE;
14892 player->drop_pressed_delay = 0;
14893 player->is_dropping_pressed = FALSE;
14895 player->drop_x = dropx;
14896 player->drop_y = dropy;
14901 /* ------------------------------------------------------------------------- */
14902 /* game sound playing functions */
14903 /* ------------------------------------------------------------------------- */
14905 static int *loop_sound_frame = NULL;
14906 static int *loop_sound_volume = NULL;
14908 void InitPlayLevelSound()
14910 int num_sounds = getSoundListSize();
14912 checked_free(loop_sound_frame);
14913 checked_free(loop_sound_volume);
14915 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
14916 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14919 static void PlayLevelSound(int x, int y, int nr)
14921 int sx = SCREENX(x), sy = SCREENY(y);
14922 int volume, stereo_position;
14923 int max_distance = 8;
14924 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14926 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14927 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14930 if (!IN_LEV_FIELD(x, y) ||
14931 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14932 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14935 volume = SOUND_MAX_VOLUME;
14937 if (!IN_SCR_FIELD(sx, sy))
14939 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14940 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14942 volume -= volume * (dx > dy ? dx : dy) / max_distance;
14945 stereo_position = (SOUND_MAX_LEFT +
14946 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14947 (SCR_FIELDX + 2 * max_distance));
14949 if (IS_LOOP_SOUND(nr))
14951 /* This assures that quieter loop sounds do not overwrite louder ones,
14952 while restarting sound volume comparison with each new game frame. */
14954 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14957 loop_sound_volume[nr] = volume;
14958 loop_sound_frame[nr] = FrameCounter;
14961 PlaySoundExt(nr, volume, stereo_position, type);
14964 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14966 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14967 x > LEVELX(BX2) ? LEVELX(BX2) : x,
14968 y < LEVELY(BY1) ? LEVELY(BY1) :
14969 y > LEVELY(BY2) ? LEVELY(BY2) : y,
14973 static void PlayLevelSoundAction(int x, int y, int action)
14975 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14978 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14980 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14982 if (sound_effect != SND_UNDEFINED)
14983 PlayLevelSound(x, y, sound_effect);
14986 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14989 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14991 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14992 PlayLevelSound(x, y, sound_effect);
14995 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14997 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14999 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15000 PlayLevelSound(x, y, sound_effect);
15003 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15005 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15007 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15008 StopSound(sound_effect);
15011 static void PlayLevelMusic()
15013 if (levelset.music[level_nr] != MUS_UNDEFINED)
15014 PlayMusic(levelset.music[level_nr]); /* from config file */
15016 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
15019 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15021 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
15022 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
15023 int x = xx - 1 - offset;
15024 int y = yy - 1 - offset;
15029 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15033 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15037 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15041 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15045 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15049 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15053 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15056 case SAMPLE_android_clone:
15057 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15060 case SAMPLE_android_move:
15061 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15064 case SAMPLE_spring:
15065 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15069 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15073 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15076 case SAMPLE_eater_eat:
15077 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15081 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15084 case SAMPLE_collect:
15085 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15088 case SAMPLE_diamond:
15089 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15092 case SAMPLE_squash:
15093 /* !!! CHECK THIS !!! */
15095 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15097 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15101 case SAMPLE_wonderfall:
15102 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15106 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15110 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15114 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15118 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15122 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15126 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15129 case SAMPLE_wonder:
15130 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15134 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15137 case SAMPLE_exit_open:
15138 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15141 case SAMPLE_exit_leave:
15142 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15145 case SAMPLE_dynamite:
15146 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15150 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15154 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15158 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15162 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15166 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15170 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15174 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15180 void ChangeTime(int value)
15182 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
15186 /* EMC game engine uses value from time counter of RND game engine */
15187 level.native_em_level->lev->time = *time;
15189 DrawGameValue_Time(*time);
15192 void RaiseScore(int value)
15194 /* EMC game engine and RND game engine have separate score counters */
15195 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
15196 &level.native_em_level->lev->score : &local_player->score);
15200 DrawGameValue_Score(*score);
15204 void RaiseScore(int value)
15206 local_player->score += value;
15209 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
15211 DisplayGameControlValues();
15213 DrawGameValue_Score(local_player->score);
15217 void RaiseScoreElement(int element)
15222 case EL_BD_DIAMOND:
15223 case EL_EMERALD_YELLOW:
15224 case EL_EMERALD_RED:
15225 case EL_EMERALD_PURPLE:
15226 case EL_SP_INFOTRON:
15227 RaiseScore(level.score[SC_EMERALD]);
15230 RaiseScore(level.score[SC_DIAMOND]);
15233 RaiseScore(level.score[SC_CRYSTAL]);
15236 RaiseScore(level.score[SC_PEARL]);
15239 case EL_BD_BUTTERFLY:
15240 case EL_SP_ELECTRON:
15241 RaiseScore(level.score[SC_BUG]);
15244 case EL_BD_FIREFLY:
15245 case EL_SP_SNIKSNAK:
15246 RaiseScore(level.score[SC_SPACESHIP]);
15249 case EL_DARK_YAMYAM:
15250 RaiseScore(level.score[SC_YAMYAM]);
15253 RaiseScore(level.score[SC_ROBOT]);
15256 RaiseScore(level.score[SC_PACMAN]);
15259 RaiseScore(level.score[SC_NUT]);
15262 case EL_EM_DYNAMITE:
15263 case EL_SP_DISK_RED:
15264 case EL_DYNABOMB_INCREASE_NUMBER:
15265 case EL_DYNABOMB_INCREASE_SIZE:
15266 case EL_DYNABOMB_INCREASE_POWER:
15267 RaiseScore(level.score[SC_DYNAMITE]);
15269 case EL_SHIELD_NORMAL:
15270 case EL_SHIELD_DEADLY:
15271 RaiseScore(level.score[SC_SHIELD]);
15273 case EL_EXTRA_TIME:
15274 RaiseScore(level.extra_time_score);
15288 case EL_DC_KEY_WHITE:
15289 RaiseScore(level.score[SC_KEY]);
15292 RaiseScore(element_info[element].collect_score);
15297 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15299 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15301 #if defined(NETWORK_AVALIABLE)
15302 if (options.network)
15303 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15312 FadeSkipNextFadeIn();
15314 fading = fading_none;
15318 OpenDoor(DOOR_CLOSE_1);
15321 game_status = GAME_MODE_MAIN;
15324 DrawAndFadeInMainMenu(REDRAW_FIELD);
15332 FadeOut(REDRAW_FIELD);
15335 game_status = GAME_MODE_MAIN;
15337 DrawAndFadeInMainMenu(REDRAW_FIELD);
15341 else /* continue playing the game */
15343 if (tape.playing && tape.deactivate_display)
15344 TapeDeactivateDisplayOff(TRUE);
15346 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15348 if (tape.playing && tape.deactivate_display)
15349 TapeDeactivateDisplayOn();
15353 void RequestQuitGame(boolean ask_if_really_quit)
15355 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15356 boolean skip_request = AllPlayersGone || quick_quit;
15358 RequestQuitGameExt(skip_request, quick_quit,
15359 "Do you really want to quit the game ?");
15363 /* ------------------------------------------------------------------------- */
15364 /* random generator functions */
15365 /* ------------------------------------------------------------------------- */
15367 unsigned int InitEngineRandom_RND(long seed)
15369 game.num_random_calls = 0;
15372 unsigned int rnd_seed = InitEngineRandom(seed);
15374 printf("::: START RND: %d\n", rnd_seed);
15379 return InitEngineRandom(seed);
15385 unsigned int RND(int max)
15389 game.num_random_calls++;
15391 return GetEngineRandom(max);
15398 /* ------------------------------------------------------------------------- */
15399 /* game engine snapshot handling functions */
15400 /* ------------------------------------------------------------------------- */
15402 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
15404 struct EngineSnapshotInfo
15406 /* runtime values for custom element collect score */
15407 int collect_score[NUM_CUSTOM_ELEMENTS];
15409 /* runtime values for group element choice position */
15410 int choice_pos[NUM_GROUP_ELEMENTS];
15412 /* runtime values for belt position animations */
15413 int belt_graphic[4 * NUM_BELT_PARTS];
15414 int belt_anim_mode[4 * NUM_BELT_PARTS];
15417 struct EngineSnapshotNodeInfo
15424 static struct EngineSnapshotInfo engine_snapshot_rnd;
15425 static ListNode *engine_snapshot_list = NULL;
15426 static char *snapshot_level_identifier = NULL;
15427 static int snapshot_level_nr = -1;
15429 void FreeEngineSnapshot()
15431 while (engine_snapshot_list != NULL)
15432 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
15435 setString(&snapshot_level_identifier, NULL);
15436 snapshot_level_nr = -1;
15439 static void SaveEngineSnapshotValues_RND()
15441 static int belt_base_active_element[4] =
15443 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15444 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15445 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15446 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15450 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15452 int element = EL_CUSTOM_START + i;
15454 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15457 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15459 int element = EL_GROUP_START + i;
15461 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15464 for (i = 0; i < 4; i++)
15466 for (j = 0; j < NUM_BELT_PARTS; j++)
15468 int element = belt_base_active_element[i] + j;
15469 int graphic = el2img(element);
15470 int anim_mode = graphic_info[graphic].anim_mode;
15472 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
15473 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
15478 static void LoadEngineSnapshotValues_RND()
15480 unsigned long num_random_calls = game.num_random_calls;
15483 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15485 int element = EL_CUSTOM_START + i;
15487 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15490 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15492 int element = EL_GROUP_START + i;
15494 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15497 for (i = 0; i < 4; i++)
15499 for (j = 0; j < NUM_BELT_PARTS; j++)
15501 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
15502 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
15504 graphic_info[graphic].anim_mode = anim_mode;
15508 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15510 InitRND(tape.random_seed);
15511 for (i = 0; i < num_random_calls; i++)
15515 if (game.num_random_calls != num_random_calls)
15517 Error(ERR_INFO, "number of random calls out of sync");
15518 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15519 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15520 Error(ERR_EXIT, "this should not happen -- please debug");
15524 static void SaveEngineSnapshotBuffer(void *buffer, int size)
15526 struct EngineSnapshotNodeInfo *bi =
15527 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
15529 bi->buffer_orig = buffer;
15530 bi->buffer_copy = checked_malloc(size);
15533 memcpy(bi->buffer_copy, buffer, size);
15535 addNodeToList(&engine_snapshot_list, NULL, bi);
15538 void SaveEngineSnapshot()
15540 FreeEngineSnapshot(); /* free previous snapshot, if needed */
15542 if (level_editor_test_game) /* do not save snapshots from editor */
15545 /* copy some special values to a structure better suited for the snapshot */
15547 SaveEngineSnapshotValues_RND();
15548 SaveEngineSnapshotValues_EM();
15550 /* save values stored in special snapshot structure */
15552 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15553 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15555 /* save further RND engine values */
15557 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
15558 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
15559 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
15561 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
15562 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
15563 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
15564 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
15566 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15567 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15568 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15569 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15570 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15572 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15573 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15574 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15576 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15578 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15580 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15581 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15583 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
15584 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
15585 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
15586 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15587 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15588 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15589 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15590 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
15591 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
15592 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15593 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
15594 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15595 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15596 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15597 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15598 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15599 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
15600 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
15602 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15603 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15605 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15606 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15607 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15609 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15610 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15612 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15613 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15614 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15615 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15616 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15618 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15619 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15621 /* save level identification information */
15623 setString(&snapshot_level_identifier, leveldir_current->identifier);
15624 snapshot_level_nr = level_nr;
15627 ListNode *node = engine_snapshot_list;
15630 while (node != NULL)
15632 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15637 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15641 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
15643 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
15646 void LoadEngineSnapshot()
15648 ListNode *node = engine_snapshot_list;
15650 if (engine_snapshot_list == NULL)
15653 while (node != NULL)
15655 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
15660 /* restore special values from snapshot structure */
15662 LoadEngineSnapshotValues_RND();
15663 LoadEngineSnapshotValues_EM();
15666 boolean CheckEngineSnapshot()
15668 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15669 snapshot_level_nr == level_nr);
15673 /* ---------- new game button stuff ---------------------------------------- */
15675 /* graphic position values for game buttons */
15676 #define GAME_BUTTON_XSIZE 30
15677 #define GAME_BUTTON_YSIZE 30
15678 #define GAME_BUTTON_XPOS 5
15679 #define GAME_BUTTON_YPOS 215
15680 #define SOUND_BUTTON_XPOS 5
15681 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
15683 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15684 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15685 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15686 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15687 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15688 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15696 } gamebutton_info[NUM_GAME_BUTTONS] =
15700 &game.button.stop.x, &game.button.stop.y,
15701 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
15706 &game.button.pause.x, &game.button.pause.y,
15707 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
15708 GAME_CTRL_ID_PAUSE,
15712 &game.button.play.x, &game.button.play.y,
15713 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
15718 &game.button.sound_music.x, &game.button.sound_music.y,
15719 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
15720 SOUND_CTRL_ID_MUSIC,
15721 "background music on/off"
15724 &game.button.sound_loops.x, &game.button.sound_loops.y,
15725 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
15726 SOUND_CTRL_ID_LOOPS,
15727 "sound loops on/off"
15730 &game.button.sound_simple.x,&game.button.sound_simple.y,
15731 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
15732 SOUND_CTRL_ID_SIMPLE,
15733 "normal sounds on/off"
15737 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
15742 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
15743 GAME_CTRL_ID_PAUSE,
15747 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
15752 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
15753 SOUND_CTRL_ID_MUSIC,
15754 "background music on/off"
15757 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
15758 SOUND_CTRL_ID_LOOPS,
15759 "sound loops on/off"
15762 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
15763 SOUND_CTRL_ID_SIMPLE,
15764 "normal sounds on/off"
15769 void CreateGameButtons()
15773 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15775 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
15776 struct GadgetInfo *gi;
15779 unsigned long event_mask;
15781 int gd_xoffset, gd_yoffset;
15782 int gd_x1, gd_x2, gd_y1, gd_y2;
15785 x = DX + *gamebutton_info[i].x;
15786 y = DY + *gamebutton_info[i].y;
15787 gd_xoffset = gamebutton_info[i].gd_x;
15788 gd_yoffset = gamebutton_info[i].gd_y;
15789 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
15790 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
15792 if (id == GAME_CTRL_ID_STOP ||
15793 id == GAME_CTRL_ID_PAUSE ||
15794 id == GAME_CTRL_ID_PLAY)
15796 button_type = GD_TYPE_NORMAL_BUTTON;
15798 event_mask = GD_EVENT_RELEASED;
15799 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15800 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15804 button_type = GD_TYPE_CHECK_BUTTON;
15806 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15807 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15808 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15809 event_mask = GD_EVENT_PRESSED;
15810 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
15811 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15814 gi = CreateGadget(GDI_CUSTOM_ID, id,
15815 GDI_INFO_TEXT, gamebutton_info[i].infotext,
15820 GDI_X, DX + gd_xoffset,
15821 GDI_Y, DY + gd_yoffset,
15823 GDI_WIDTH, GAME_BUTTON_XSIZE,
15824 GDI_HEIGHT, GAME_BUTTON_YSIZE,
15825 GDI_TYPE, button_type,
15826 GDI_STATE, GD_BUTTON_UNPRESSED,
15827 GDI_CHECKED, checked,
15828 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
15829 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
15830 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
15831 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
15832 GDI_DIRECT_DRAW, FALSE,
15833 GDI_EVENT_MASK, event_mask,
15834 GDI_CALLBACK_ACTION, HandleGameButtons,
15838 Error(ERR_EXIT, "cannot create gadget");
15840 game_gadget[id] = gi;
15844 void FreeGameButtons()
15848 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15849 FreeGadget(game_gadget[i]);
15852 static void MapGameButtons()
15856 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15857 MapGadget(game_gadget[i]);
15860 void UnmapGameButtons()
15864 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15865 UnmapGadget(game_gadget[i]);
15868 void RedrawGameButtons()
15872 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15873 RedrawGadget(game_gadget[i]);
15876 static void HandleGameButtons(struct GadgetInfo *gi)
15878 int id = gi->custom_id;
15880 if (game_status != GAME_MODE_PLAYING)
15885 case GAME_CTRL_ID_STOP:
15889 RequestQuitGame(TRUE);
15892 case GAME_CTRL_ID_PAUSE:
15893 if (options.network)
15895 #if defined(NETWORK_AVALIABLE)
15897 SendToServer_ContinuePlaying();
15899 SendToServer_PausePlaying();
15903 TapeTogglePause(TAPE_TOGGLE_MANUAL);
15906 case GAME_CTRL_ID_PLAY:
15909 #if defined(NETWORK_AVALIABLE)
15910 if (options.network)
15911 SendToServer_ContinuePlaying();
15915 tape.pausing = FALSE;
15916 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
15921 case SOUND_CTRL_ID_MUSIC:
15922 if (setup.sound_music)
15924 setup.sound_music = FALSE;
15927 else if (audio.music_available)
15929 setup.sound = setup.sound_music = TRUE;
15931 SetAudioMode(setup.sound);
15937 case SOUND_CTRL_ID_LOOPS:
15938 if (setup.sound_loops)
15939 setup.sound_loops = FALSE;
15940 else if (audio.loops_available)
15942 setup.sound = setup.sound_loops = TRUE;
15943 SetAudioMode(setup.sound);
15947 case SOUND_CTRL_ID_SIMPLE:
15948 if (setup.sound_simple)
15949 setup.sound_simple = FALSE;
15950 else if (audio.sound_available)
15952 setup.sound = setup.sound_simple = TRUE;
15953 SetAudioMode(setup.sound);