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;
1946 case EL_TRIGGER_PLAYER:
1947 case EL_TRIGGER_ELEMENT:
1948 case EL_TRIGGER_CE_VALUE:
1949 case EL_TRIGGER_CE_SCORE:
1951 case EL_ANY_ELEMENT:
1952 case EL_CURRENT_CE_VALUE:
1953 case EL_CURRENT_CE_SCORE:
1970 /* reference elements should not be used on the playfield */
1971 Feld[x][y] = EL_EMPTY;
1975 if (IS_CUSTOM_ELEMENT(element))
1977 if (CAN_MOVE(element))
1980 #if USE_NEW_CUSTOM_VALUE
1981 if (!element_info[element].use_last_ce_value || init_game)
1982 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1985 else if (IS_GROUP_ELEMENT(element))
1987 Feld[x][y] = GetElementFromGroupElement(element);
1989 InitField(x, y, init_game);
1996 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1999 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2001 InitField(x, y, init_game);
2003 /* not needed to call InitMovDir() -- already done by InitField()! */
2004 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2005 CAN_MOVE(Feld[x][y]))
2009 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2011 int old_element = Feld[x][y];
2013 InitField(x, y, init_game);
2015 /* not needed to call InitMovDir() -- already done by InitField()! */
2016 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2017 CAN_MOVE(old_element) &&
2018 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2021 /* this case is in fact a combination of not less than three bugs:
2022 first, it calls InitMovDir() for elements that can move, although this is
2023 already done by InitField(); then, it checks the element that was at this
2024 field _before_ the call to InitField() (which can change it); lastly, it
2025 was not called for "mole with direction" elements, which were treated as
2026 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2032 static int get_key_element_from_nr(int key_nr)
2034 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2035 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2036 EL_EM_KEY_1 : EL_KEY_1);
2038 return key_base_element + key_nr;
2041 static int get_next_dropped_element(struct PlayerInfo *player)
2043 return (player->inventory_size > 0 ?
2044 player->inventory_element[player->inventory_size - 1] :
2045 player->inventory_infinite_element != EL_UNDEFINED ?
2046 player->inventory_infinite_element :
2047 player->dynabombs_left > 0 ?
2048 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2052 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2054 /* pos >= 0: get element from bottom of the stack;
2055 pos < 0: get element from top of the stack */
2059 int min_inventory_size = -pos;
2060 int inventory_pos = player->inventory_size - min_inventory_size;
2061 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2063 return (player->inventory_size >= min_inventory_size ?
2064 player->inventory_element[inventory_pos] :
2065 player->inventory_infinite_element != EL_UNDEFINED ?
2066 player->inventory_infinite_element :
2067 player->dynabombs_left >= min_dynabombs_left ?
2068 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2073 int min_dynabombs_left = pos + 1;
2074 int min_inventory_size = pos + 1 - player->dynabombs_left;
2075 int inventory_pos = pos - player->dynabombs_left;
2077 return (player->inventory_infinite_element != EL_UNDEFINED ?
2078 player->inventory_infinite_element :
2079 player->dynabombs_left >= min_dynabombs_left ?
2080 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2081 player->inventory_size >= min_inventory_size ?
2082 player->inventory_element[inventory_pos] :
2087 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2089 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2090 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2093 if (gpo1->sort_priority != gpo2->sort_priority)
2094 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2096 compare_result = gpo1->nr - gpo2->nr;
2098 return compare_result;
2101 void InitGameControlValues()
2105 for (i = 0; game_panel_controls[i].nr != -1; i++)
2107 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2108 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2109 struct TextPosInfo *pos = gpc->pos;
2111 int type = gpc->type;
2115 Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2116 Error(ERR_EXIT, "this should not happen -- please debug");
2119 /* force update of game controls after initialization */
2120 gpc->value = gpc->last_value = -1;
2121 gpc->frame = gpc->last_frame = -1;
2122 gpc->gfx_frame = -1;
2124 /* determine panel value width for later calculation of alignment */
2125 if (type == TYPE_INTEGER || type == TYPE_STRING)
2127 pos->width = pos->size * getFontWidth(pos->font);
2128 pos->height = getFontHeight(pos->font);
2130 else if (type == TYPE_ELEMENT)
2132 pos->width = pos->size;
2133 pos->height = pos->size;
2136 /* fill structure for game panel draw order */
2138 gpo->sort_priority = pos->sort_priority;
2141 /* sort game panel controls according to sort_priority and control number */
2142 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2143 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2146 void UpdatePlayfieldElementCount()
2148 boolean use_element_count = FALSE;
2151 /* first check if it is needed at all to calculate playfield element count */
2152 for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2153 if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2154 use_element_count = TRUE;
2156 if (!use_element_count)
2159 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2160 element_info[i].element_count = 0;
2162 SCAN_PLAYFIELD(x, y)
2164 element_info[Feld[x][y]].element_count++;
2167 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2168 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2169 if (IS_IN_GROUP(j, i))
2170 element_info[EL_GROUP_START + i].element_count +=
2171 element_info[j].element_count;
2174 void UpdateGameControlValues()
2177 int time = (local_player->LevelSolved ?
2178 local_player->LevelSolved_CountingTime :
2179 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2180 level.native_em_level->lev->time :
2181 level.time == 0 ? TimePlayed : TimeLeft);
2182 int score = (local_player->LevelSolved ?
2183 local_player->LevelSolved_CountingScore :
2184 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2185 level.native_em_level->lev->score :
2186 local_player->score);
2187 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2188 level.native_em_level->lev->required :
2189 local_player->gems_still_needed);
2190 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2191 level.native_em_level->lev->required > 0 :
2192 local_player->gems_still_needed > 0 ||
2193 local_player->sokobanfields_still_needed > 0 ||
2194 local_player->lights_still_needed > 0);
2196 UpdatePlayfieldElementCount();
2198 /* update game panel control values */
2200 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2201 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2203 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2204 for (i = 0; i < MAX_NUM_KEYS; i++)
2205 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2206 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2207 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2209 if (game.centered_player_nr == -1)
2211 for (i = 0; i < MAX_PLAYERS; i++)
2213 for (k = 0; k < MAX_NUM_KEYS; k++)
2215 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2217 if (level.native_em_level->ply[i]->keys & (1 << k))
2218 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2219 get_key_element_from_nr(k);
2221 else if (stored_player[i].key[k])
2222 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2223 get_key_element_from_nr(k);
2226 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2227 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2228 level.native_em_level->ply[i]->dynamite;
2230 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2231 stored_player[i].inventory_size;
2233 if (stored_player[i].num_white_keys > 0)
2234 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2237 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2238 stored_player[i].num_white_keys;
2243 int player_nr = game.centered_player_nr;
2245 for (k = 0; k < MAX_NUM_KEYS; k++)
2247 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2249 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2250 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2251 get_key_element_from_nr(k);
2253 else if (stored_player[player_nr].key[k])
2254 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2255 get_key_element_from_nr(k);
2258 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2259 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2260 level.native_em_level->ply[player_nr]->dynamite;
2262 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2263 stored_player[player_nr].inventory_size;
2265 if (stored_player[player_nr].num_white_keys > 0)
2266 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2268 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2269 stored_player[player_nr].num_white_keys;
2272 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2274 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2275 get_inventory_element_from_pos(local_player, i);
2276 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2277 get_inventory_element_from_pos(local_player, -i - 1);
2280 game_panel_controls[GAME_PANEL_SCORE].value = score;
2281 game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2283 game_panel_controls[GAME_PANEL_TIME].value = time;
2285 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2286 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2287 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2289 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2290 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2292 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2293 local_player->shield_normal_time_left;
2294 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2295 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2297 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2298 local_player->shield_deadly_time_left;
2300 game_panel_controls[GAME_PANEL_EXIT].value =
2301 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2303 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2304 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2305 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2306 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2307 EL_EMC_MAGIC_BALL_SWITCH);
2309 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2310 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2311 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2312 game.light_time_left;
2314 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2315 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2316 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2317 game.timegate_time_left;
2319 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2320 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2322 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2323 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2324 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2325 game.lenses_time_left;
2327 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2328 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2329 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2330 game.magnify_time_left;
2332 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2333 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2334 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2335 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2336 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2337 EL_BALLOON_SWITCH_NONE);
2339 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2340 local_player->dynabomb_count;
2341 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2342 local_player->dynabomb_size;
2343 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2344 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2346 game_panel_controls[GAME_PANEL_PENGUINS].value =
2347 local_player->friends_still_needed;
2349 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2350 local_player->sokobanfields_still_needed;
2351 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2352 local_player->sokobanfields_still_needed;
2354 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2355 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2357 for (i = 0; i < NUM_BELTS; i++)
2359 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2360 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2361 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2362 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2363 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2366 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2367 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2368 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2369 game.magic_wall_time_left;
2371 #if USE_PLAYER_GRAVITY
2372 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2373 local_player->gravity;
2375 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2378 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2379 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2381 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2382 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2383 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2384 game.panel.element[i].id : EL_UNDEFINED);
2386 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2387 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2388 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2389 element_info[game.panel.element_count[i].id].element_count : 0);
2391 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2392 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2393 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2394 element_info[game.panel.ce_score[i].id].collect_score : 0);
2396 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2397 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2398 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2399 element_info[game.panel.ce_score_element[i].id].collect_score :
2402 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2403 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2404 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2406 /* update game panel control frames */
2408 for (i = 0; game_panel_controls[i].nr != -1; i++)
2410 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2412 if (gpc->type == TYPE_ELEMENT)
2414 int last_anim_random_frame = gfx.anim_random_frame;
2415 int element = gpc->value;
2416 int graphic = el2panelimg(element);
2418 if (gpc->value != gpc->last_value)
2421 gpc->gfx_random = INIT_GFX_RANDOM();
2427 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2428 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2429 gpc->gfx_random = INIT_GFX_RANDOM();
2432 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2433 gfx.anim_random_frame = gpc->gfx_random;
2435 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2436 gpc->gfx_frame = element_info[element].collect_score;
2438 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2441 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2442 gfx.anim_random_frame = last_anim_random_frame;
2447 void DisplayGameControlValues()
2449 boolean redraw_panel = FALSE;
2452 for (i = 0; game_panel_controls[i].nr != -1; i++)
2454 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2456 if (PANEL_DEACTIVATED(gpc->pos))
2459 if (gpc->value == gpc->last_value &&
2460 gpc->frame == gpc->last_frame)
2463 redraw_panel = TRUE;
2469 /* copy default game door content to main double buffer */
2470 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2471 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2473 /* redraw game control buttons */
2475 RedrawGameButtons();
2481 game_status = GAME_MODE_PSEUDO_PANEL;
2484 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2486 for (i = 0; game_panel_controls[i].nr != -1; i++)
2490 int nr = game_panel_order[i].nr;
2491 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2493 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2496 struct TextPosInfo *pos = gpc->pos;
2497 int type = gpc->type;
2498 int value = gpc->value;
2499 int frame = gpc->frame;
2501 int last_value = gpc->last_value;
2502 int last_frame = gpc->last_frame;
2504 int size = pos->size;
2505 int font = pos->font;
2506 boolean draw_masked = pos->draw_masked;
2507 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2509 if (PANEL_DEACTIVATED(pos))
2513 if (value == last_value && frame == last_frame)
2517 gpc->last_value = value;
2518 gpc->last_frame = frame;
2521 printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2524 if (type == TYPE_INTEGER)
2526 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2527 nr == GAME_PANEL_TIME)
2529 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2531 if (use_dynamic_size) /* use dynamic number of digits */
2533 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2534 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2535 int size2 = size1 + 1;
2536 int font1 = pos->font;
2537 int font2 = pos->font_alt;
2539 size = (value < value_change ? size1 : size2);
2540 font = (value < value_change ? font1 : font2);
2543 /* clear background if value just changed its size (dynamic digits) */
2544 if ((last_value < value_change) != (value < value_change))
2546 int width1 = size1 * getFontWidth(font1);
2547 int width2 = size2 * getFontWidth(font2);
2548 int max_width = MAX(width1, width2);
2549 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2551 pos->width = max_width;
2553 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2554 max_width, max_height);
2561 /* correct text size if "digits" is zero or less */
2563 size = strlen(int2str(value, size));
2565 /* dynamically correct text alignment */
2566 pos->width = size * getFontWidth(font);
2569 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2570 int2str(value, size), font, mask_mode);
2572 else if (type == TYPE_ELEMENT)
2574 int element, graphic;
2578 int dst_x = PANEL_XPOS(pos);
2579 int dst_y = PANEL_YPOS(pos);
2582 if (value != EL_UNDEFINED && value != EL_EMPTY)
2585 graphic = el2panelimg(value);
2587 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2590 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2594 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2597 width = graphic_info[graphic].width * size / TILESIZE;
2598 height = graphic_info[graphic].height * size / TILESIZE;
2602 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2603 dst_x - src_x, dst_y - src_y);
2604 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2609 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2614 if (value == EL_UNDEFINED || value == EL_EMPTY)
2616 element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2617 graphic = el2panelimg(element);
2619 src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2620 src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2621 src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2626 graphic = el2panelimg(value);
2628 getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2631 width = graphic_info[graphic].width * size / TILESIZE;
2632 height = graphic_info[graphic].height * size / TILESIZE;
2634 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2637 else if (type == TYPE_STRING)
2639 boolean active = (value != 0);
2640 char *state_normal = "off";
2641 char *state_active = "on";
2642 char *state = (active ? state_active : state_normal);
2643 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2644 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2645 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2646 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2648 if (nr == GAME_PANEL_GRAVITY_STATE)
2650 int font1 = pos->font; /* (used for normal state) */
2651 int font2 = pos->font_alt; /* (used for active state) */
2653 int size1 = strlen(state_normal);
2654 int size2 = strlen(state_active);
2655 int width1 = size1 * getFontWidth(font1);
2656 int width2 = size2 * getFontWidth(font2);
2657 int max_width = MAX(width1, width2);
2658 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2660 pos->width = max_width;
2662 /* clear background for values that may have changed its size */
2663 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2664 max_width, max_height);
2667 font = (active ? font2 : font1);
2677 /* don't truncate output if "chars" is zero or less */
2680 /* dynamically correct text alignment */
2681 pos->width = size * getFontWidth(font);
2685 s_cut = getStringCopyN(s, size);
2687 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2688 s_cut, font, mask_mode);
2694 redraw_mask |= REDRAW_DOOR_1;
2697 game_status = GAME_MODE_PLAYING;
2700 void UpdateAndDisplayGameControlValues()
2702 if (tape.warp_forward)
2705 UpdateGameControlValues();
2706 DisplayGameControlValues();
2709 void DrawGameValue_Emeralds(int value)
2711 struct TextPosInfo *pos = &game.panel.gems;
2713 int font_nr = pos->font;
2715 int font_nr = FONT_TEXT_2;
2717 int font_width = getFontWidth(font_nr);
2718 int chars = pos->size;
2721 return; /* !!! USE NEW STUFF !!! */
2724 if (PANEL_DEACTIVATED(pos))
2727 pos->width = chars * font_width;
2729 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2732 void DrawGameValue_Dynamite(int value)
2734 struct TextPosInfo *pos = &game.panel.inventory_count;
2736 int font_nr = pos->font;
2738 int font_nr = FONT_TEXT_2;
2740 int font_width = getFontWidth(font_nr);
2741 int chars = pos->size;
2744 return; /* !!! USE NEW STUFF !!! */
2747 if (PANEL_DEACTIVATED(pos))
2750 pos->width = chars * font_width;
2752 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2755 void DrawGameValue_Score(int value)
2757 struct TextPosInfo *pos = &game.panel.score;
2759 int font_nr = pos->font;
2761 int font_nr = FONT_TEXT_2;
2763 int font_width = getFontWidth(font_nr);
2764 int chars = pos->size;
2767 return; /* !!! USE NEW STUFF !!! */
2770 if (PANEL_DEACTIVATED(pos))
2773 pos->width = chars * font_width;
2775 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2778 void DrawGameValue_Time(int value)
2780 struct TextPosInfo *pos = &game.panel.time;
2781 static int last_value = -1;
2784 int chars = pos->size;
2786 int font1_nr = pos->font;
2787 int font2_nr = pos->font_alt;
2789 int font1_nr = FONT_TEXT_2;
2790 int font2_nr = FONT_TEXT_1;
2792 int font_nr = font1_nr;
2793 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2796 return; /* !!! USE NEW STUFF !!! */
2799 if (PANEL_DEACTIVATED(pos))
2802 if (use_dynamic_chars) /* use dynamic number of chars */
2804 chars = (value < 1000 ? chars1 : chars2);
2805 font_nr = (value < 1000 ? font1_nr : font2_nr);
2808 /* clear background if value just changed its size (dynamic chars only) */
2809 if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2811 int width1 = chars1 * getFontWidth(font1_nr);
2812 int width2 = chars2 * getFontWidth(font2_nr);
2813 int max_width = MAX(width1, width2);
2814 int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2816 pos->width = max_width;
2818 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2819 max_width, max_height);
2822 pos->width = chars * getFontWidth(font_nr);
2824 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2829 void DrawGameValue_Level(int value)
2831 struct TextPosInfo *pos = &game.panel.level_number;
2834 int chars = pos->size;
2836 int font1_nr = pos->font;
2837 int font2_nr = pos->font_alt;
2839 int font1_nr = FONT_TEXT_2;
2840 int font2_nr = FONT_TEXT_1;
2842 int font_nr = font1_nr;
2843 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2846 return; /* !!! USE NEW STUFF !!! */
2849 if (PANEL_DEACTIVATED(pos))
2852 if (use_dynamic_chars) /* use dynamic number of chars */
2854 chars = (level_nr < 100 ? chars1 : chars2);
2855 font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2858 pos->width = chars * getFontWidth(font_nr);
2860 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2863 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2866 struct TextPosInfo *pos = &game.panel.keys;
2869 int base_key_graphic = EL_KEY_1;
2874 return; /* !!! USE NEW STUFF !!! */
2878 if (PANEL_DEACTIVATED(pos))
2883 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2884 base_key_graphic = EL_EM_KEY_1;
2888 pos->width = 4 * MINI_TILEX;
2892 for (i = 0; i < MAX_NUM_KEYS; i++)
2894 /* currently only 4 of 8 possible keys are displayed */
2895 for (i = 0; i < STD_NUM_KEYS; i++)
2899 struct TextPosInfo *pos = &game.panel.key[i];
2901 int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2902 int src_y = DOOR_GFX_PAGEY1 + 123;
2904 int dst_x = PANEL_XPOS(pos);
2905 int dst_y = PANEL_YPOS(pos);
2907 int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2908 int dst_y = PANEL_YPOS(pos);
2912 int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2913 level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2915 int graphic = el2edimg(element);
2919 if (PANEL_DEACTIVATED(pos))
2924 /* masked blit with tiles from half-size scaled bitmap does not work yet
2925 (no mask bitmap created for these sizes after loading and scaling) --
2926 solution: load without creating mask, scale, then create final mask */
2928 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2929 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2934 int graphic = el2edimg(base_key_graphic + i);
2939 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2941 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2942 dst_x - src_x, dst_y - src_y);
2943 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2949 DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2951 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2952 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2955 DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
2957 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2958 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2966 void DrawGameValue_Emeralds(int value)
2968 int font_nr = FONT_TEXT_2;
2969 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2971 if (PANEL_DEACTIVATED(game.panel.gems))
2974 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
2977 void DrawGameValue_Dynamite(int value)
2979 int font_nr = FONT_TEXT_2;
2980 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2982 if (PANEL_DEACTIVATED(game.panel.inventory_count))
2985 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
2988 void DrawGameValue_Score(int value)
2990 int font_nr = FONT_TEXT_2;
2991 int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
2993 if (PANEL_DEACTIVATED(game.panel.score))
2996 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
2999 void DrawGameValue_Time(int value)
3001 int font1_nr = FONT_TEXT_2;
3003 int font2_nr = FONT_TEXT_1;
3005 int font2_nr = FONT_LEVEL_NUMBER;
3007 int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
3008 int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
3010 if (PANEL_DEACTIVATED(game.panel.time))
3013 /* clear background if value just changed its size */
3014 if (value == 999 || value == 1000)
3015 ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
3018 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
3020 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
3023 void DrawGameValue_Level(int value)
3025 int font1_nr = FONT_TEXT_2;
3027 int font2_nr = FONT_TEXT_1;
3029 int font2_nr = FONT_LEVEL_NUMBER;
3032 if (PANEL_DEACTIVATED(game.panel.level))
3036 DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
3038 DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
3041 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
3043 int base_key_graphic = EL_KEY_1;
3046 if (PANEL_DEACTIVATED(game.panel.keys))
3049 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3050 base_key_graphic = EL_EM_KEY_1;
3052 /* currently only 4 of 8 possible keys are displayed */
3053 for (i = 0; i < STD_NUM_KEYS; i++)
3055 int x = XX_KEYS + i * MINI_TILEX;
3059 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3061 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3062 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3068 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3071 int key[MAX_NUM_KEYS];
3074 /* prevent EM engine from updating time/score values parallel to GameWon() */
3075 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3076 local_player->LevelSolved)
3079 for (i = 0; i < MAX_NUM_KEYS; i++)
3080 key[i] = key_bits & (1 << i);
3082 DrawGameValue_Level(level_nr);
3084 DrawGameValue_Emeralds(emeralds);
3085 DrawGameValue_Dynamite(dynamite);
3086 DrawGameValue_Score(score);
3087 DrawGameValue_Time(time);
3089 DrawGameValue_Keys(key);
3092 void UpdateGameDoorValues()
3094 UpdateGameControlValues();
3097 void DrawGameDoorValues()
3099 DisplayGameControlValues();
3102 void DrawGameDoorValues_OLD()
3104 int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
3105 int dynamite_value = 0;
3106 int score_value = (local_player->LevelSolved ? local_player->score_final :
3107 local_player->score);
3108 int gems_value = local_player->gems_still_needed;
3112 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3114 DrawGameDoorValues_EM();
3119 if (game.centered_player_nr == -1)
3121 for (i = 0; i < MAX_PLAYERS; i++)
3123 for (j = 0; j < MAX_NUM_KEYS; j++)
3124 if (stored_player[i].key[j])
3125 key_bits |= (1 << j);
3127 dynamite_value += stored_player[i].inventory_size;
3132 int player_nr = game.centered_player_nr;
3134 for (i = 0; i < MAX_NUM_KEYS; i++)
3135 if (stored_player[player_nr].key[i])
3136 key_bits |= (1 << i);
3138 dynamite_value = stored_player[player_nr].inventory_size;
3141 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3147 =============================================================================
3149 -----------------------------------------------------------------------------
3150 initialize game engine due to level / tape version number
3151 =============================================================================
3154 static void InitGameEngine()
3156 int i, j, k, l, x, y;
3158 /* set game engine from tape file when re-playing, else from level file */
3159 game.engine_version = (tape.playing ? tape.engine_version :
3160 level.game_version);
3162 /* ---------------------------------------------------------------------- */
3163 /* set flags for bugs and changes according to active game engine version */
3164 /* ---------------------------------------------------------------------- */
3167 Summary of bugfix/change:
3168 Fixed handling for custom elements that change when pushed by the player.
3170 Fixed/changed in version:
3174 Before 3.1.0, custom elements that "change when pushing" changed directly
3175 after the player started pushing them (until then handled in "DigField()").
3176 Since 3.1.0, these custom elements are not changed until the "pushing"
3177 move of the element is finished (now handled in "ContinueMoving()").
3179 Affected levels/tapes:
3180 The first condition is generally needed for all levels/tapes before version
3181 3.1.0, which might use the old behaviour before it was changed; known tapes
3182 that are affected are some tapes from the level set "Walpurgis Gardens" by
3184 The second condition is an exception from the above case and is needed for
3185 the special case of tapes recorded with game (not engine!) version 3.1.0 or
3186 above (including some development versions of 3.1.0), but before it was
3187 known that this change would break tapes like the above and was fixed in
3188 3.1.1, so that the changed behaviour was active although the engine version
3189 while recording maybe was before 3.1.0. There is at least one tape that is
3190 affected by this exception, which is the tape for the one-level set "Bug
3191 Machine" by Juergen Bonhagen.
3194 game.use_change_when_pushing_bug =
3195 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3197 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3198 tape.game_version < VERSION_IDENT(3,1,1,0)));
3201 Summary of bugfix/change:
3202 Fixed handling for blocking the field the player leaves when moving.
3204 Fixed/changed in version:
3208 Before 3.1.1, when "block last field when moving" was enabled, the field
3209 the player is leaving when moving was blocked for the time of the move,
3210 and was directly unblocked afterwards. This resulted in the last field
3211 being blocked for exactly one less than the number of frames of one player
3212 move. Additionally, even when blocking was disabled, the last field was
3213 blocked for exactly one frame.
3214 Since 3.1.1, due to changes in player movement handling, the last field
3215 is not blocked at all when blocking is disabled. When blocking is enabled,
3216 the last field is blocked for exactly the number of frames of one player
3217 move. Additionally, if the player is Murphy, the hero of Supaplex, the
3218 last field is blocked for exactly one more than the number of frames of
3221 Affected levels/tapes:
3222 (!!! yet to be determined -- probably many !!!)
3225 game.use_block_last_field_bug =
3226 (game.engine_version < VERSION_IDENT(3,1,1,0));
3229 Summary of bugfix/change:
3230 Changed behaviour of CE changes with multiple changes per single frame.
3232 Fixed/changed in version:
3236 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3237 This resulted in race conditions where CEs seem to behave strange in some
3238 situations (where triggered CE changes were just skipped because there was
3239 already a CE change on that tile in the playfield in that engine frame).
3240 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3241 (The number of changes per frame must be limited in any case, because else
3242 it is easily possible to define CE changes that would result in an infinite
3243 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3244 should be set large enough so that it would only be reached in cases where
3245 the corresponding CE change conditions run into a loop. Therefore, it seems
3246 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3247 maximal number of change pages for custom elements.)
3249 Affected levels/tapes:
3253 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3254 game.max_num_changes_per_frame = 1;
3256 game.max_num_changes_per_frame =
3257 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3260 /* ---------------------------------------------------------------------- */
3262 /* default scan direction: scan playfield from top/left to bottom/right */
3263 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3265 /* dynamically adjust element properties according to game engine version */
3266 InitElementPropertiesEngine(game.engine_version);
3269 printf("level %d: level version == %06d\n", level_nr, level.game_version);
3270 printf(" tape version == %06d [%s] [file: %06d]\n",
3271 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3273 printf(" => game.engine_version == %06d\n", game.engine_version);
3276 /* ---------- initialize player's initial move delay --------------------- */
3278 /* dynamically adjust player properties according to level information */
3279 for (i = 0; i < MAX_PLAYERS; i++)
3280 game.initial_move_delay_value[i] =
3281 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3283 /* dynamically adjust player properties according to game engine version */
3284 for (i = 0; i < MAX_PLAYERS; i++)
3285 game.initial_move_delay[i] =
3286 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3287 game.initial_move_delay_value[i] : 0);
3289 /* ---------- initialize player's initial push delay --------------------- */
3291 /* dynamically adjust player properties according to game engine version */
3292 game.initial_push_delay_value =
3293 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3295 /* ---------- initialize changing elements ------------------------------- */
3297 /* initialize changing elements information */
3298 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3300 struct ElementInfo *ei = &element_info[i];
3302 /* this pointer might have been changed in the level editor */
3303 ei->change = &ei->change_page[0];
3305 if (!IS_CUSTOM_ELEMENT(i))
3307 ei->change->target_element = EL_EMPTY_SPACE;
3308 ei->change->delay_fixed = 0;
3309 ei->change->delay_random = 0;
3310 ei->change->delay_frames = 1;
3313 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3315 ei->has_change_event[j] = FALSE;
3317 ei->event_page_nr[j] = 0;
3318 ei->event_page[j] = &ei->change_page[0];
3322 /* add changing elements from pre-defined list */
3323 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3325 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3326 struct ElementInfo *ei = &element_info[ch_delay->element];
3328 ei->change->target_element = ch_delay->target_element;
3329 ei->change->delay_fixed = ch_delay->change_delay;
3331 ei->change->pre_change_function = ch_delay->pre_change_function;
3332 ei->change->change_function = ch_delay->change_function;
3333 ei->change->post_change_function = ch_delay->post_change_function;
3335 ei->change->can_change = TRUE;
3336 ei->change->can_change_or_has_action = TRUE;
3338 ei->has_change_event[CE_DELAY] = TRUE;
3340 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3341 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3344 /* ---------- initialize internal run-time variables --------------------- */
3346 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3348 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3350 for (j = 0; j < ei->num_change_pages; j++)
3352 ei->change_page[j].can_change_or_has_action =
3353 (ei->change_page[j].can_change |
3354 ei->change_page[j].has_action);
3358 /* add change events from custom element configuration */
3359 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3361 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3363 for (j = 0; j < ei->num_change_pages; j++)
3365 if (!ei->change_page[j].can_change_or_has_action)
3368 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3370 /* only add event page for the first page found with this event */
3371 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3373 ei->has_change_event[k] = TRUE;
3375 ei->event_page_nr[k] = j;
3376 ei->event_page[k] = &ei->change_page[j];
3383 /* ---------- initialize reference elements in change conditions --------- */
3385 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3387 int element = EL_CUSTOM_START + i;
3388 struct ElementInfo *ei = &element_info[element];
3390 for (j = 0; j < ei->num_change_pages; j++)
3392 int trigger_element = ei->change_page[j].initial_trigger_element;
3394 if (trigger_element >= EL_PREV_CE_8 &&
3395 trigger_element <= EL_NEXT_CE_8)
3396 trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3398 ei->change_page[j].trigger_element = trigger_element;
3403 /* ---------- initialize run-time trigger player and element ------------- */
3405 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3407 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3409 for (j = 0; j < ei->num_change_pages; j++)
3411 ei->change_page[j].actual_trigger_element = EL_EMPTY;
3412 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
3413 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_1;
3414 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3415 ei->change_page[j].actual_trigger_ce_value = 0;
3416 ei->change_page[j].actual_trigger_ce_score = 0;
3420 /* ---------- initialize trigger events ---------------------------------- */
3422 /* initialize trigger events information */
3423 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3424 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3425 trigger_events[i][j] = FALSE;
3427 /* add trigger events from element change event properties */
3428 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3430 struct ElementInfo *ei = &element_info[i];
3432 for (j = 0; j < ei->num_change_pages; j++)
3434 if (!ei->change_page[j].can_change_or_has_action)
3437 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3439 int trigger_element = ei->change_page[j].trigger_element;
3441 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3443 if (ei->change_page[j].has_event[k])
3445 if (IS_GROUP_ELEMENT(trigger_element))
3447 struct ElementGroupInfo *group =
3448 element_info[trigger_element].group;
3450 for (l = 0; l < group->num_elements_resolved; l++)
3451 trigger_events[group->element_resolved[l]][k] = TRUE;
3453 else if (trigger_element == EL_ANY_ELEMENT)
3454 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3455 trigger_events[l][k] = TRUE;
3457 trigger_events[trigger_element][k] = TRUE;
3464 /* ---------- initialize push delay -------------------------------------- */
3466 /* initialize push delay values to default */
3467 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3469 if (!IS_CUSTOM_ELEMENT(i))
3471 /* set default push delay values (corrected since version 3.0.7-1) */
3472 if (game.engine_version < VERSION_IDENT(3,0,7,1))
3474 element_info[i].push_delay_fixed = 2;
3475 element_info[i].push_delay_random = 8;
3479 element_info[i].push_delay_fixed = 8;
3480 element_info[i].push_delay_random = 8;
3485 /* set push delay value for certain elements from pre-defined list */
3486 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3488 int e = push_delay_list[i].element;
3490 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
3491 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3494 /* set push delay value for Supaplex elements for newer engine versions */
3495 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3497 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3499 if (IS_SP_ELEMENT(i))
3501 /* set SP push delay to just enough to push under a falling zonk */
3502 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3504 element_info[i].push_delay_fixed = delay;
3505 element_info[i].push_delay_random = 0;
3510 /* ---------- initialize move stepsize ----------------------------------- */
3512 /* initialize move stepsize values to default */
3513 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3514 if (!IS_CUSTOM_ELEMENT(i))
3515 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3517 /* set move stepsize value for certain elements from pre-defined list */
3518 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3520 int e = move_stepsize_list[i].element;
3522 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3525 /* ---------- initialize collect score ----------------------------------- */
3527 /* initialize collect score values for custom elements from initial value */
3528 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3529 if (IS_CUSTOM_ELEMENT(i))
3530 element_info[i].collect_score = element_info[i].collect_score_initial;
3532 /* ---------- initialize collect count ----------------------------------- */
3534 /* initialize collect count values for non-custom elements */
3535 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3536 if (!IS_CUSTOM_ELEMENT(i))
3537 element_info[i].collect_count_initial = 0;
3539 /* add collect count values for all elements from pre-defined list */
3540 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3541 element_info[collect_count_list[i].element].collect_count_initial =
3542 collect_count_list[i].count;
3544 /* ---------- initialize access direction -------------------------------- */
3546 /* initialize access direction values to default (access from every side) */
3547 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3548 if (!IS_CUSTOM_ELEMENT(i))
3549 element_info[i].access_direction = MV_ALL_DIRECTIONS;
3551 /* set access direction value for certain elements from pre-defined list */
3552 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3553 element_info[access_direction_list[i].element].access_direction =
3554 access_direction_list[i].direction;
3556 /* ---------- initialize explosion content ------------------------------- */
3557 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3559 if (IS_CUSTOM_ELEMENT(i))
3562 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3564 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3566 element_info[i].content.e[x][y] =
3567 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3568 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3569 i == EL_PLAYER_3 ? EL_EMERALD :
3570 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3571 i == EL_MOLE ? EL_EMERALD_RED :
3572 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3573 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3574 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3575 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3576 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3577 i == EL_WALL_EMERALD ? EL_EMERALD :
3578 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3579 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3580 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3581 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3582 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3583 i == EL_WALL_PEARL ? EL_PEARL :
3584 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3589 /* ---------- initialize recursion detection ------------------------------ */
3590 recursion_loop_depth = 0;
3591 recursion_loop_detected = FALSE;
3592 recursion_loop_element = EL_UNDEFINED;
3594 /* ---------- initialize graphics engine ---------------------------------- */
3595 game.scroll_delay_value =
3596 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3597 setup.scroll_delay ? setup.scroll_delay_value : 0);
3598 game.scroll_delay_value =
3599 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3602 int get_num_special_action(int element, int action_first, int action_last)
3604 int num_special_action = 0;
3607 for (i = action_first; i <= action_last; i++)
3609 boolean found = FALSE;
3611 for (j = 0; j < NUM_DIRECTIONS; j++)
3612 if (el_act_dir2img(element, i, j) !=
3613 el_act_dir2img(element, ACTION_DEFAULT, j))
3617 num_special_action++;
3622 return num_special_action;
3627 =============================================================================
3629 -----------------------------------------------------------------------------
3630 initialize and start new game
3631 =============================================================================
3636 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3637 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3638 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3640 boolean do_fading = (game_status == GAME_MODE_MAIN);
3644 game_status = GAME_MODE_PLAYING;
3647 InitGameControlValues();
3649 /* don't play tapes over network */
3650 network_playing = (options.network && !tape.playing);
3652 for (i = 0; i < MAX_PLAYERS; i++)
3654 struct PlayerInfo *player = &stored_player[i];
3656 player->index_nr = i;
3657 player->index_bit = (1 << i);
3658 player->element_nr = EL_PLAYER_1 + i;
3660 player->present = FALSE;
3661 player->active = FALSE;
3662 player->killed = FALSE;
3665 player->effective_action = 0;
3666 player->programmed_action = 0;
3669 player->score_final = 0;
3671 player->gems_still_needed = level.gems_needed;
3672 player->sokobanfields_still_needed = 0;
3673 player->lights_still_needed = 0;
3674 player->friends_still_needed = 0;
3676 for (j = 0; j < MAX_NUM_KEYS; j++)
3677 player->key[j] = FALSE;
3679 player->num_white_keys = 0;
3681 player->dynabomb_count = 0;
3682 player->dynabomb_size = 1;
3683 player->dynabombs_left = 0;
3684 player->dynabomb_xl = FALSE;
3686 player->MovDir = MV_NONE;
3689 player->GfxDir = MV_NONE;
3690 player->GfxAction = ACTION_DEFAULT;
3692 player->StepFrame = 0;
3694 player->use_murphy = FALSE;
3695 player->artwork_element =
3696 (level.use_artwork_element[i] ? level.artwork_element[i] :
3697 player->element_nr);
3699 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3700 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3702 player->gravity = level.initial_player_gravity[i];
3704 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3706 player->actual_frame_counter = 0;
3708 player->step_counter = 0;
3710 player->last_move_dir = MV_NONE;
3712 player->is_active = FALSE;
3714 player->is_waiting = FALSE;
3715 player->is_moving = FALSE;
3716 player->is_auto_moving = FALSE;
3717 player->is_digging = FALSE;
3718 player->is_snapping = FALSE;
3719 player->is_collecting = FALSE;
3720 player->is_pushing = FALSE;
3721 player->is_switching = FALSE;
3722 player->is_dropping = FALSE;
3723 player->is_dropping_pressed = FALSE;
3725 player->is_bored = FALSE;
3726 player->is_sleeping = FALSE;
3728 player->frame_counter_bored = -1;
3729 player->frame_counter_sleeping = -1;
3731 player->anim_delay_counter = 0;
3732 player->post_delay_counter = 0;
3734 player->dir_waiting = MV_NONE;
3735 player->action_waiting = ACTION_DEFAULT;
3736 player->last_action_waiting = ACTION_DEFAULT;
3737 player->special_action_bored = ACTION_DEFAULT;
3738 player->special_action_sleeping = ACTION_DEFAULT;
3740 player->switch_x = -1;
3741 player->switch_y = -1;
3743 player->drop_x = -1;
3744 player->drop_y = -1;
3746 player->show_envelope = 0;
3748 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3750 player->push_delay = -1; /* initialized when pushing starts */
3751 player->push_delay_value = game.initial_push_delay_value;
3753 player->drop_delay = 0;
3754 player->drop_pressed_delay = 0;
3756 player->last_jx = -1;
3757 player->last_jy = -1;
3761 player->shield_normal_time_left = 0;
3762 player->shield_deadly_time_left = 0;
3764 player->inventory_infinite_element = EL_UNDEFINED;
3765 player->inventory_size = 0;
3767 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3768 SnapField(player, 0, 0);
3770 player->LevelSolved = FALSE;
3771 player->GameOver = FALSE;
3773 player->LevelSolved_GameWon = FALSE;
3774 player->LevelSolved_GameEnd = FALSE;
3775 player->LevelSolved_PanelOff = FALSE;
3776 player->LevelSolved_SaveTape = FALSE;
3777 player->LevelSolved_SaveScore = FALSE;
3778 player->LevelSolved_CountingTime = 0;
3779 player->LevelSolved_CountingScore = 0;
3782 network_player_action_received = FALSE;
3784 #if defined(NETWORK_AVALIABLE)
3785 /* initial null action */
3786 if (network_playing)
3787 SendToServer_MovePlayer(MV_NONE);
3796 TimeLeft = level.time;
3799 ScreenMovDir = MV_NONE;
3803 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3805 AllPlayersGone = FALSE;
3807 game.yamyam_content_nr = 0;
3808 game.robot_wheel_active = FALSE;
3809 game.magic_wall_active = FALSE;
3810 game.magic_wall_time_left = 0;
3811 game.light_time_left = 0;
3812 game.timegate_time_left = 0;
3813 game.switchgate_pos = 0;
3814 game.wind_direction = level.wind_direction_initial;
3816 #if !USE_PLAYER_GRAVITY
3817 game.gravity = FALSE;
3818 game.explosions_delayed = TRUE;
3821 game.lenses_time_left = 0;
3822 game.magnify_time_left = 0;
3824 game.ball_state = level.ball_state_initial;
3825 game.ball_content_nr = 0;
3827 game.envelope_active = FALSE;
3829 /* set focus to local player for network games, else to all players */
3830 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3831 game.centered_player_nr_next = game.centered_player_nr;
3832 game.set_centered_player = FALSE;
3834 if (network_playing && tape.recording)
3836 /* store client dependent player focus when recording network games */
3837 tape.centered_player_nr_next = game.centered_player_nr_next;
3838 tape.set_centered_player = TRUE;
3841 for (i = 0; i < NUM_BELTS; i++)
3843 game.belt_dir[i] = MV_NONE;
3844 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3847 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3848 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3850 SCAN_PLAYFIELD(x, y)
3852 Feld[x][y] = level.field[x][y];
3853 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3854 ChangeDelay[x][y] = 0;
3855 ChangePage[x][y] = -1;
3856 #if USE_NEW_CUSTOM_VALUE
3857 CustomValue[x][y] = 0; /* initialized in InitField() */
3859 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3861 WasJustMoving[x][y] = 0;
3862 WasJustFalling[x][y] = 0;
3863 CheckCollision[x][y] = 0;
3864 CheckImpact[x][y] = 0;
3866 Pushed[x][y] = FALSE;
3868 ChangeCount[x][y] = 0;
3869 ChangeEvent[x][y] = -1;
3871 ExplodePhase[x][y] = 0;
3872 ExplodeDelay[x][y] = 0;
3873 ExplodeField[x][y] = EX_TYPE_NONE;
3875 RunnerVisit[x][y] = 0;
3876 PlayerVisit[x][y] = 0;
3879 GfxRandom[x][y] = INIT_GFX_RANDOM();
3880 GfxElement[x][y] = EL_UNDEFINED;
3881 GfxAction[x][y] = ACTION_DEFAULT;
3882 GfxDir[x][y] = MV_NONE;
3885 SCAN_PLAYFIELD(x, y)
3887 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3889 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3891 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3894 InitField(x, y, TRUE);
3896 ResetGfxAnimation(x, y);
3901 for (i = 0; i < MAX_PLAYERS; i++)
3903 struct PlayerInfo *player = &stored_player[i];
3905 /* set number of special actions for bored and sleeping animation */
3906 player->num_special_action_bored =
3907 get_num_special_action(player->artwork_element,
3908 ACTION_BORING_1, ACTION_BORING_LAST);
3909 player->num_special_action_sleeping =
3910 get_num_special_action(player->artwork_element,
3911 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3914 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3915 emulate_sb ? EMU_SOKOBAN :
3916 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3918 #if USE_NEW_ALL_SLIPPERY
3919 /* initialize type of slippery elements */
3920 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3922 if (!IS_CUSTOM_ELEMENT(i))
3924 /* default: elements slip down either to the left or right randomly */
3925 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3927 /* SP style elements prefer to slip down on the left side */
3928 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3929 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3931 /* BD style elements prefer to slip down on the left side */
3932 if (game.emulation == EMU_BOULDERDASH)
3933 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3938 /* initialize explosion and ignition delay */
3939 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3941 if (!IS_CUSTOM_ELEMENT(i))
3944 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3945 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3946 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3947 int last_phase = (num_phase + 1) * delay;
3948 int half_phase = (num_phase / 2) * delay;
3950 element_info[i].explosion_delay = last_phase - 1;
3951 element_info[i].ignition_delay = half_phase;
3953 if (i == EL_BLACK_ORB)
3954 element_info[i].ignition_delay = 1;
3958 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
3959 element_info[i].explosion_delay = 1;
3961 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
3962 element_info[i].ignition_delay = 1;
3966 /* correct non-moving belts to start moving left */
3967 for (i = 0; i < NUM_BELTS; i++)
3968 if (game.belt_dir[i] == MV_NONE)
3969 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3971 /* check if any connected player was not found in playfield */
3972 for (i = 0; i < MAX_PLAYERS; i++)
3974 struct PlayerInfo *player = &stored_player[i];
3976 if (player->connected && !player->present)
3978 for (j = 0; j < MAX_PLAYERS; j++)
3980 struct PlayerInfo *some_player = &stored_player[j];
3981 int jx = some_player->jx, jy = some_player->jy;
3983 /* assign first free player found that is present in the playfield */
3984 if (some_player->present && !some_player->connected)
3986 player->present = TRUE;
3987 player->active = TRUE;
3989 some_player->present = FALSE;
3990 some_player->active = FALSE;
3992 player->artwork_element = some_player->artwork_element;
3994 player->block_last_field = some_player->block_last_field;
3995 player->block_delay_adjustment = some_player->block_delay_adjustment;
3997 StorePlayer[jx][jy] = player->element_nr;
3998 player->jx = player->last_jx = jx;
3999 player->jy = player->last_jy = jy;
4009 /* when playing a tape, eliminate all players who do not participate */
4011 for (i = 0; i < MAX_PLAYERS; i++)
4013 if (stored_player[i].active && !tape.player_participates[i])
4015 struct PlayerInfo *player = &stored_player[i];
4016 int jx = player->jx, jy = player->jy;
4018 player->active = FALSE;
4019 StorePlayer[jx][jy] = 0;
4020 Feld[jx][jy] = EL_EMPTY;
4024 else if (!options.network && !setup.team_mode) /* && !tape.playing */
4026 /* when in single player mode, eliminate all but the first active player */
4028 for (i = 0; i < MAX_PLAYERS; i++)
4030 if (stored_player[i].active)
4032 for (j = i + 1; j < MAX_PLAYERS; j++)
4034 if (stored_player[j].active)
4036 struct PlayerInfo *player = &stored_player[j];
4037 int jx = player->jx, jy = player->jy;
4039 player->active = FALSE;
4040 player->present = FALSE;
4042 StorePlayer[jx][jy] = 0;
4043 Feld[jx][jy] = EL_EMPTY;
4050 /* when recording the game, store which players take part in the game */
4053 for (i = 0; i < MAX_PLAYERS; i++)
4054 if (stored_player[i].active)
4055 tape.player_participates[i] = TRUE;
4060 for (i = 0; i < MAX_PLAYERS; i++)
4062 struct PlayerInfo *player = &stored_player[i];
4064 printf("Player %d: present == %d, connected == %d, active == %d.\n",
4069 if (local_player == player)
4070 printf("Player %d is local player.\n", i+1);
4074 if (BorderElement == EL_EMPTY)
4077 SBX_Right = lev_fieldx - SCR_FIELDX;
4079 SBY_Lower = lev_fieldy - SCR_FIELDY;
4084 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4086 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4089 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4090 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4092 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4093 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4095 /* if local player not found, look for custom element that might create
4096 the player (make some assumptions about the right custom element) */
4097 if (!local_player->present)
4099 int start_x = 0, start_y = 0;
4100 int found_rating = 0;
4101 int found_element = EL_UNDEFINED;
4102 int player_nr = local_player->index_nr;
4104 SCAN_PLAYFIELD(x, y)
4106 int element = Feld[x][y];
4111 if (level.use_start_element[player_nr] &&
4112 level.start_element[player_nr] == element &&
4119 found_element = element;
4122 if (!IS_CUSTOM_ELEMENT(element))
4125 if (CAN_CHANGE(element))
4127 for (i = 0; i < element_info[element].num_change_pages; i++)
4129 /* check for player created from custom element as single target */
4130 content = element_info[element].change_page[i].target_element;
4131 is_player = ELEM_IS_PLAYER(content);
4133 if (is_player && (found_rating < 3 ||
4134 (found_rating == 3 && element < found_element)))
4140 found_element = element;
4145 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4147 /* check for player created from custom element as explosion content */
4148 content = element_info[element].content.e[xx][yy];
4149 is_player = ELEM_IS_PLAYER(content);
4151 if (is_player && (found_rating < 2 ||
4152 (found_rating == 2 && element < found_element)))
4154 start_x = x + xx - 1;
4155 start_y = y + yy - 1;
4158 found_element = element;
4161 if (!CAN_CHANGE(element))
4164 for (i = 0; i < element_info[element].num_change_pages; i++)
4166 /* check for player created from custom element as extended target */
4168 element_info[element].change_page[i].target_content.e[xx][yy];
4170 is_player = ELEM_IS_PLAYER(content);
4172 if (is_player && (found_rating < 1 ||
4173 (found_rating == 1 && element < found_element)))
4175 start_x = x + xx - 1;
4176 start_y = y + yy - 1;
4179 found_element = element;
4185 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
4186 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4189 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4190 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4195 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
4196 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4197 local_player->jx - MIDPOSX);
4199 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4200 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4201 local_player->jy - MIDPOSY);
4205 /* do not use PLAYING mask for fading out from main screen */
4206 game_status = GAME_MODE_MAIN;
4211 if (!game.restart_level)
4212 CloseDoor(DOOR_CLOSE_1);
4215 if (level_editor_test_game)
4216 FadeSkipNextFadeIn();
4218 FadeSetEnterScreen();
4220 if (level_editor_test_game)
4221 fading = fading_none;
4223 fading = menu.destination;
4227 FadeOut(REDRAW_FIELD);
4230 FadeOut(REDRAW_FIELD);
4234 game_status = GAME_MODE_PLAYING;
4237 /* !!! FIX THIS (START) !!! */
4238 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4240 InitGameEngine_EM();
4242 /* blit playfield from scroll buffer to normal back buffer for fading in */
4243 BlitScreenToBitmap_EM(backbuffer);
4250 /* after drawing the level, correct some elements */
4251 if (game.timegate_time_left == 0)
4252 CloseAllOpenTimegates();
4254 /* blit playfield from scroll buffer to normal back buffer for fading in */
4255 if (setup.soft_scrolling)
4256 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4258 redraw_mask |= REDRAW_FROM_BACKBUFFER;
4260 /* !!! FIX THIS (END) !!! */
4263 FadeIn(REDRAW_FIELD);
4266 FadeIn(REDRAW_FIELD);
4271 if (!game.restart_level)
4273 /* copy default game door content to main double buffer */
4274 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4275 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4278 SetPanelBackground();
4279 SetDrawBackgroundMask(REDRAW_DOOR_1);
4282 UpdateAndDisplayGameControlValues();
4284 UpdateGameDoorValues();
4285 DrawGameDoorValues();
4288 if (!game.restart_level)
4292 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4293 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4294 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4298 /* copy actual game door content to door double buffer for OpenDoor() */
4299 BlitBitmap(drawto, bitmap_db_door,
4300 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4302 OpenDoor(DOOR_OPEN_ALL);
4304 PlaySound(SND_GAME_STARTING);
4306 if (setup.sound_music)
4309 KeyboardAutoRepeatOffUnlessAutoplay();
4313 for (i = 0; i < MAX_PLAYERS; i++)
4314 printf("Player %d %sactive.\n",
4315 i + 1, (stored_player[i].active ? "" : "not "));
4326 game.restart_level = FALSE;
4329 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4331 /* this is used for non-R'n'D game engines to update certain engine values */
4333 /* needed to determine if sounds are played within the visible screen area */
4334 scroll_x = actual_scroll_x;
4335 scroll_y = actual_scroll_y;
4338 void InitMovDir(int x, int y)
4340 int i, element = Feld[x][y];
4341 static int xy[4][2] =
4348 static int direction[3][4] =
4350 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4351 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4352 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4361 Feld[x][y] = EL_BUG;
4362 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4365 case EL_SPACESHIP_RIGHT:
4366 case EL_SPACESHIP_UP:
4367 case EL_SPACESHIP_LEFT:
4368 case EL_SPACESHIP_DOWN:
4369 Feld[x][y] = EL_SPACESHIP;
4370 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4373 case EL_BD_BUTTERFLY_RIGHT:
4374 case EL_BD_BUTTERFLY_UP:
4375 case EL_BD_BUTTERFLY_LEFT:
4376 case EL_BD_BUTTERFLY_DOWN:
4377 Feld[x][y] = EL_BD_BUTTERFLY;
4378 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4381 case EL_BD_FIREFLY_RIGHT:
4382 case EL_BD_FIREFLY_UP:
4383 case EL_BD_FIREFLY_LEFT:
4384 case EL_BD_FIREFLY_DOWN:
4385 Feld[x][y] = EL_BD_FIREFLY;
4386 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4389 case EL_PACMAN_RIGHT:
4391 case EL_PACMAN_LEFT:
4392 case EL_PACMAN_DOWN:
4393 Feld[x][y] = EL_PACMAN;
4394 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4397 case EL_YAMYAM_LEFT:
4398 case EL_YAMYAM_RIGHT:
4400 case EL_YAMYAM_DOWN:
4401 Feld[x][y] = EL_YAMYAM;
4402 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4405 case EL_SP_SNIKSNAK:
4406 MovDir[x][y] = MV_UP;
4409 case EL_SP_ELECTRON:
4410 MovDir[x][y] = MV_LEFT;
4417 Feld[x][y] = EL_MOLE;
4418 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4422 if (IS_CUSTOM_ELEMENT(element))
4424 struct ElementInfo *ei = &element_info[element];
4425 int move_direction_initial = ei->move_direction_initial;
4426 int move_pattern = ei->move_pattern;
4428 if (move_direction_initial == MV_START_PREVIOUS)
4430 if (MovDir[x][y] != MV_NONE)
4433 move_direction_initial = MV_START_AUTOMATIC;
4436 if (move_direction_initial == MV_START_RANDOM)
4437 MovDir[x][y] = 1 << RND(4);
4438 else if (move_direction_initial & MV_ANY_DIRECTION)
4439 MovDir[x][y] = move_direction_initial;
4440 else if (move_pattern == MV_ALL_DIRECTIONS ||
4441 move_pattern == MV_TURNING_LEFT ||
4442 move_pattern == MV_TURNING_RIGHT ||
4443 move_pattern == MV_TURNING_LEFT_RIGHT ||
4444 move_pattern == MV_TURNING_RIGHT_LEFT ||
4445 move_pattern == MV_TURNING_RANDOM)
4446 MovDir[x][y] = 1 << RND(4);
4447 else if (move_pattern == MV_HORIZONTAL)
4448 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4449 else if (move_pattern == MV_VERTICAL)
4450 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4451 else if (move_pattern & MV_ANY_DIRECTION)
4452 MovDir[x][y] = element_info[element].move_pattern;
4453 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4454 move_pattern == MV_ALONG_RIGHT_SIDE)
4456 /* use random direction as default start direction */
4457 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4458 MovDir[x][y] = 1 << RND(4);
4460 for (i = 0; i < NUM_DIRECTIONS; i++)
4462 int x1 = x + xy[i][0];
4463 int y1 = y + xy[i][1];
4465 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4467 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4468 MovDir[x][y] = direction[0][i];
4470 MovDir[x][y] = direction[1][i];
4479 MovDir[x][y] = 1 << RND(4);
4481 if (element != EL_BUG &&
4482 element != EL_SPACESHIP &&
4483 element != EL_BD_BUTTERFLY &&
4484 element != EL_BD_FIREFLY)
4487 for (i = 0; i < NUM_DIRECTIONS; i++)
4489 int x1 = x + xy[i][0];
4490 int y1 = y + xy[i][1];
4492 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4494 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4496 MovDir[x][y] = direction[0][i];
4499 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4500 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4502 MovDir[x][y] = direction[1][i];
4511 GfxDir[x][y] = MovDir[x][y];
4514 void InitAmoebaNr(int x, int y)
4517 int group_nr = AmoebeNachbarNr(x, y);
4521 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4523 if (AmoebaCnt[i] == 0)
4531 AmoebaNr[x][y] = group_nr;
4532 AmoebaCnt[group_nr]++;
4533 AmoebaCnt2[group_nr]++;
4536 static void PlayerWins(struct PlayerInfo *player)
4538 player->LevelSolved = TRUE;
4539 player->GameOver = TRUE;
4541 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4542 level.native_em_level->lev->score : player->score);
4544 player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4545 player->LevelSolved_CountingScore = player->score_final;
4550 static int time, time_final;
4551 static int score, score_final;
4552 static int game_over_delay_1 = 0;
4553 static int game_over_delay_2 = 0;
4554 int game_over_delay_value_1 = 50;
4555 int game_over_delay_value_2 = 50;
4557 if (!local_player->LevelSolved_GameWon)
4561 /* do not start end game actions before the player stops moving (to exit) */
4562 if (local_player->MovPos)
4565 local_player->LevelSolved_GameWon = TRUE;
4566 local_player->LevelSolved_SaveTape = tape.recording;
4567 local_player->LevelSolved_SaveScore = !tape.playing;
4569 if (tape.auto_play) /* tape might already be stopped here */
4570 tape.auto_play_level_solved = TRUE;
4576 game_over_delay_1 = game_over_delay_value_1;
4577 game_over_delay_2 = game_over_delay_value_2;
4579 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4580 score = score_final = local_player->score_final;
4585 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4587 else if (level.time == 0 && TimePlayed < 999)
4590 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4593 local_player->score_final = score_final;
4595 if (level_editor_test_game)
4598 score = score_final;
4601 local_player->LevelSolved_CountingTime = time;
4602 local_player->LevelSolved_CountingScore = score;
4604 game_panel_controls[GAME_PANEL_TIME].value = time;
4605 game_panel_controls[GAME_PANEL_SCORE].value = score;
4607 DisplayGameControlValues();
4609 DrawGameValue_Time(time);
4610 DrawGameValue_Score(score);
4614 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4616 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4618 /* close exit door after last player */
4619 if ((AllPlayersGone &&
4620 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4621 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4622 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4623 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4624 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4626 int element = Feld[ExitX][ExitY];
4629 if (element == EL_EM_EXIT_OPEN ||
4630 element == EL_EM_STEEL_EXIT_OPEN)
4637 Feld[ExitX][ExitY] =
4638 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4639 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4640 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4641 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4642 EL_EM_STEEL_EXIT_CLOSING);
4644 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4648 /* player disappears */
4649 DrawLevelField(ExitX, ExitY);
4652 for (i = 0; i < MAX_PLAYERS; i++)
4654 struct PlayerInfo *player = &stored_player[i];
4656 if (player->present)
4658 RemovePlayer(player);
4660 /* player disappears */
4661 DrawLevelField(player->jx, player->jy);
4666 PlaySound(SND_GAME_WINNING);
4669 if (game_over_delay_1 > 0)
4671 game_over_delay_1--;
4676 if (time != time_final)
4678 int time_to_go = ABS(time_final - time);
4679 int time_count_dir = (time < time_final ? +1 : -1);
4680 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4682 time += time_count_steps * time_count_dir;
4683 score += time_count_steps * level.score[SC_TIME_BONUS];
4686 local_player->LevelSolved_CountingTime = time;
4687 local_player->LevelSolved_CountingScore = score;
4689 game_panel_controls[GAME_PANEL_TIME].value = time;
4690 game_panel_controls[GAME_PANEL_SCORE].value = score;
4692 DisplayGameControlValues();
4694 DrawGameValue_Time(time);
4695 DrawGameValue_Score(score);
4698 if (time == time_final)
4699 StopSound(SND_GAME_LEVELTIME_BONUS);
4700 else if (setup.sound_loops)
4701 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4703 PlaySound(SND_GAME_LEVELTIME_BONUS);
4708 local_player->LevelSolved_PanelOff = TRUE;
4710 if (game_over_delay_2 > 0)
4712 game_over_delay_2--;
4725 boolean raise_level = FALSE;
4727 local_player->LevelSolved_GameEnd = TRUE;
4729 CloseDoor(DOOR_CLOSE_1);
4731 if (local_player->LevelSolved_SaveTape)
4738 SaveTapeChecked(tape.level_nr); /* ask to save tape */
4740 SaveTape(tape.level_nr); /* ask to save tape */
4744 if (level_editor_test_game)
4746 game_status = GAME_MODE_MAIN;
4749 DrawAndFadeInMainMenu(REDRAW_FIELD);
4757 if (!local_player->LevelSolved_SaveScore)
4760 FadeOut(REDRAW_FIELD);
4763 game_status = GAME_MODE_MAIN;
4765 DrawAndFadeInMainMenu(REDRAW_FIELD);
4770 if (level_nr == leveldir_current->handicap_level)
4772 leveldir_current->handicap_level++;
4773 SaveLevelSetup_SeriesInfo();
4776 if (level_nr < leveldir_current->last_level)
4777 raise_level = TRUE; /* advance to next level */
4779 if ((hi_pos = NewHiScore()) >= 0)
4781 game_status = GAME_MODE_SCORES;
4783 DrawHallOfFame(hi_pos);
4794 FadeOut(REDRAW_FIELD);
4797 game_status = GAME_MODE_MAIN;
4805 DrawAndFadeInMainMenu(REDRAW_FIELD);
4814 LoadScore(level_nr);
4816 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4817 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4820 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4822 if (local_player->score_final > highscore[k].Score)
4824 /* player has made it to the hall of fame */
4826 if (k < MAX_SCORE_ENTRIES - 1)
4828 int m = MAX_SCORE_ENTRIES - 1;
4831 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4832 if (strEqual(setup.player_name, highscore[l].Name))
4834 if (m == k) /* player's new highscore overwrites his old one */
4838 for (l = m; l > k; l--)
4840 strcpy(highscore[l].Name, highscore[l - 1].Name);
4841 highscore[l].Score = highscore[l - 1].Score;
4848 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4849 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4850 highscore[k].Score = local_player->score_final;
4856 else if (!strncmp(setup.player_name, highscore[k].Name,
4857 MAX_PLAYER_NAME_LEN))
4858 break; /* player already there with a higher score */
4864 SaveScore(level_nr);
4869 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4871 int element = Feld[x][y];
4872 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4873 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4874 int horiz_move = (dx != 0);
4875 int sign = (horiz_move ? dx : dy);
4876 int step = sign * element_info[element].move_stepsize;
4878 /* special values for move stepsize for spring and things on conveyor belt */
4881 if (CAN_FALL(element) &&
4882 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4883 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4884 else if (element == EL_SPRING)
4885 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4891 inline static int getElementMoveStepsize(int x, int y)
4893 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4896 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4898 if (player->GfxAction != action || player->GfxDir != dir)
4901 printf("Player frame reset! (%d => %d, %d => %d)\n",
4902 player->GfxAction, action, player->GfxDir, dir);
4905 player->GfxAction = action;
4906 player->GfxDir = dir;
4908 player->StepFrame = 0;
4912 #if USE_GFX_RESET_GFX_ANIMATION
4913 static void ResetGfxFrame(int x, int y, boolean redraw)
4915 int element = Feld[x][y];
4916 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4917 int last_gfx_frame = GfxFrame[x][y];
4919 if (graphic_info[graphic].anim_global_sync)
4920 GfxFrame[x][y] = FrameCounter;
4921 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4922 GfxFrame[x][y] = CustomValue[x][y];
4923 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4924 GfxFrame[x][y] = element_info[element].collect_score;
4925 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4926 GfxFrame[x][y] = ChangeDelay[x][y];
4928 if (redraw && GfxFrame[x][y] != last_gfx_frame)
4929 DrawLevelGraphicAnimation(x, y, graphic);
4933 static void ResetGfxAnimation(int x, int y)
4935 GfxAction[x][y] = ACTION_DEFAULT;
4936 GfxDir[x][y] = MovDir[x][y];
4939 #if USE_GFX_RESET_GFX_ANIMATION
4940 ResetGfxFrame(x, y, FALSE);
4944 static void ResetRandomAnimationValue(int x, int y)
4946 GfxRandom[x][y] = INIT_GFX_RANDOM();
4949 void InitMovingField(int x, int y, int direction)
4951 int element = Feld[x][y];
4952 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4953 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4956 boolean is_moving_before, is_moving_after;
4958 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
4961 /* check if element was/is moving or being moved before/after mode change */
4964 is_moving_before = (WasJustMoving[x][y] != 0);
4966 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
4967 is_moving_before = WasJustMoving[x][y];
4970 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
4972 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
4974 /* reset animation only for moving elements which change direction of moving
4975 or which just started or stopped moving
4976 (else CEs with property "can move" / "not moving" are reset each frame) */
4977 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4979 if (is_moving_before != is_moving_after ||
4980 direction != MovDir[x][y])
4981 ResetGfxAnimation(x, y);
4983 if ((is_moving_before || is_moving_after) && !continues_moving)
4984 ResetGfxAnimation(x, y);
4987 if (!continues_moving)
4988 ResetGfxAnimation(x, y);
4991 MovDir[x][y] = direction;
4992 GfxDir[x][y] = direction;
4994 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4995 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4996 direction == MV_DOWN && CAN_FALL(element) ?
4997 ACTION_FALLING : ACTION_MOVING);
4999 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5000 ACTION_FALLING : ACTION_MOVING);
5003 /* this is needed for CEs with property "can move" / "not moving" */
5005 if (is_moving_after)
5007 if (Feld[newx][newy] == EL_EMPTY)
5008 Feld[newx][newy] = EL_BLOCKED;
5010 MovDir[newx][newy] = MovDir[x][y];
5012 #if USE_NEW_CUSTOM_VALUE
5013 CustomValue[newx][newy] = CustomValue[x][y];
5016 GfxFrame[newx][newy] = GfxFrame[x][y];
5017 GfxRandom[newx][newy] = GfxRandom[x][y];
5018 GfxAction[newx][newy] = GfxAction[x][y];
5019 GfxDir[newx][newy] = GfxDir[x][y];
5023 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5025 int direction = MovDir[x][y];
5026 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5027 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
5033 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5035 int oldx = x, oldy = y;
5036 int direction = MovDir[x][y];
5038 if (direction == MV_LEFT)
5040 else if (direction == MV_RIGHT)
5042 else if (direction == MV_UP)
5044 else if (direction == MV_DOWN)
5047 *comes_from_x = oldx;
5048 *comes_from_y = oldy;
5051 int MovingOrBlocked2Element(int x, int y)
5053 int element = Feld[x][y];
5055 if (element == EL_BLOCKED)
5059 Blocked2Moving(x, y, &oldx, &oldy);
5060 return Feld[oldx][oldy];
5066 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5068 /* like MovingOrBlocked2Element(), but if element is moving
5069 and (x,y) is the field the moving element is just leaving,
5070 return EL_BLOCKED instead of the element value */
5071 int element = Feld[x][y];
5073 if (IS_MOVING(x, y))
5075 if (element == EL_BLOCKED)
5079 Blocked2Moving(x, y, &oldx, &oldy);
5080 return Feld[oldx][oldy];
5089 static void RemoveField(int x, int y)
5091 Feld[x][y] = EL_EMPTY;
5097 #if USE_NEW_CUSTOM_VALUE
5098 CustomValue[x][y] = 0;
5102 ChangeDelay[x][y] = 0;
5103 ChangePage[x][y] = -1;
5104 Pushed[x][y] = FALSE;
5107 ExplodeField[x][y] = EX_TYPE_NONE;
5110 GfxElement[x][y] = EL_UNDEFINED;
5111 GfxAction[x][y] = ACTION_DEFAULT;
5112 GfxDir[x][y] = MV_NONE;
5115 void RemoveMovingField(int x, int y)
5117 int oldx = x, oldy = y, newx = x, newy = y;
5118 int element = Feld[x][y];
5119 int next_element = EL_UNDEFINED;
5121 if (element != EL_BLOCKED && !IS_MOVING(x, y))
5124 if (IS_MOVING(x, y))
5126 Moving2Blocked(x, y, &newx, &newy);
5128 if (Feld[newx][newy] != EL_BLOCKED)
5130 /* element is moving, but target field is not free (blocked), but
5131 already occupied by something different (example: acid pool);
5132 in this case, only remove the moving field, but not the target */
5134 RemoveField(oldx, oldy);
5136 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5138 DrawLevelField(oldx, oldy);
5143 else if (element == EL_BLOCKED)
5145 Blocked2Moving(x, y, &oldx, &oldy);
5146 if (!IS_MOVING(oldx, oldy))
5150 if (element == EL_BLOCKED &&
5151 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5152 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5153 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5154 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5155 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5156 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5157 next_element = get_next_element(Feld[oldx][oldy]);
5159 RemoveField(oldx, oldy);
5160 RemoveField(newx, newy);
5162 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5164 if (next_element != EL_UNDEFINED)
5165 Feld[oldx][oldy] = next_element;
5167 DrawLevelField(oldx, oldy);
5168 DrawLevelField(newx, newy);
5171 void DrawDynamite(int x, int y)
5173 int sx = SCREENX(x), sy = SCREENY(y);
5174 int graphic = el2img(Feld[x][y]);
5177 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5180 if (IS_WALKABLE_INSIDE(Back[x][y]))
5184 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5185 else if (Store[x][y])
5186 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5188 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5190 if (Back[x][y] || Store[x][y])
5191 DrawGraphicThruMask(sx, sy, graphic, frame);
5193 DrawGraphic(sx, sy, graphic, frame);
5196 void CheckDynamite(int x, int y)
5198 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5202 if (MovDelay[x][y] != 0)
5205 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5211 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5216 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5218 boolean num_checked_players = 0;
5221 for (i = 0; i < MAX_PLAYERS; i++)
5223 if (stored_player[i].active)
5225 int sx = stored_player[i].jx;
5226 int sy = stored_player[i].jy;
5228 if (num_checked_players == 0)
5235 *sx1 = MIN(*sx1, sx);
5236 *sy1 = MIN(*sy1, sy);
5237 *sx2 = MAX(*sx2, sx);
5238 *sy2 = MAX(*sy2, sy);
5241 num_checked_players++;
5246 static boolean checkIfAllPlayersFitToScreen_RND()
5248 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5250 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5252 return (sx2 - sx1 < SCR_FIELDX &&
5253 sy2 - sy1 < SCR_FIELDY);
5256 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5258 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5260 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5262 *sx = (sx1 + sx2) / 2;
5263 *sy = (sy1 + sy2) / 2;
5266 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5267 boolean center_screen, boolean quick_relocation)
5269 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5270 boolean no_delay = (tape.warp_forward);
5271 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5272 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5274 if (quick_relocation)
5276 int offset = game.scroll_delay_value;
5278 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5280 if (!level.shifted_relocation || center_screen)
5282 /* quick relocation (without scrolling), with centering of screen */
5284 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5285 x > SBX_Right + MIDPOSX ? SBX_Right :
5288 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5289 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5294 /* quick relocation (without scrolling), but do not center screen */
5296 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5297 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5300 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5301 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5304 int offset_x = x + (scroll_x - center_scroll_x);
5305 int offset_y = y + (scroll_y - center_scroll_y);
5307 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5308 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5309 offset_x - MIDPOSX);
5311 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5312 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5313 offset_y - MIDPOSY);
5318 /* quick relocation (without scrolling), inside visible screen area */
5320 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
5321 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5322 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5324 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
5325 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5326 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5328 /* don't scroll over playfield boundaries */
5329 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5330 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5332 /* don't scroll over playfield boundaries */
5333 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5334 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5337 RedrawPlayfield(TRUE, 0,0,0,0);
5342 int scroll_xx, scroll_yy;
5344 if (!level.shifted_relocation || center_screen)
5346 /* visible relocation (with scrolling), with centering of screen */
5348 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5349 x > SBX_Right + MIDPOSX ? SBX_Right :
5352 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5353 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5358 /* visible relocation (with scrolling), but do not center screen */
5360 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5361 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5364 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5365 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5368 int offset_x = x + (scroll_x - center_scroll_x);
5369 int offset_y = y + (scroll_y - center_scroll_y);
5371 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5372 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5373 offset_x - MIDPOSX);
5375 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5376 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5377 offset_y - MIDPOSY);
5382 /* visible relocation (with scrolling), with centering of screen */
5384 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5385 x > SBX_Right + MIDPOSX ? SBX_Right :
5388 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5389 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5393 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5395 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5398 int fx = FX, fy = FY;
5400 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5401 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5403 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5409 fx += dx * TILEX / 2;
5410 fy += dy * TILEY / 2;
5412 ScrollLevel(dx, dy);
5415 /* scroll in two steps of half tile size to make things smoother */
5416 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5418 Delay(wait_delay_value);
5420 /* scroll second step to align at full tile size */
5422 Delay(wait_delay_value);
5427 Delay(wait_delay_value);
5431 void RelocatePlayer(int jx, int jy, int el_player_raw)
5433 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5434 int player_nr = GET_PLAYER_NR(el_player);
5435 struct PlayerInfo *player = &stored_player[player_nr];
5436 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5437 boolean no_delay = (tape.warp_forward);
5438 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5439 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5440 int old_jx = player->jx;
5441 int old_jy = player->jy;
5442 int old_element = Feld[old_jx][old_jy];
5443 int element = Feld[jx][jy];
5444 boolean player_relocated = (old_jx != jx || old_jy != jy);
5446 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5447 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5448 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5449 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5450 int leave_side_horiz = move_dir_horiz;
5451 int leave_side_vert = move_dir_vert;
5452 int enter_side = enter_side_horiz | enter_side_vert;
5453 int leave_side = leave_side_horiz | leave_side_vert;
5455 if (player->GameOver) /* do not reanimate dead player */
5458 if (!player_relocated) /* no need to relocate the player */
5461 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5463 RemoveField(jx, jy); /* temporarily remove newly placed player */
5464 DrawLevelField(jx, jy);
5467 if (player->present)
5469 while (player->MovPos)
5471 ScrollPlayer(player, SCROLL_GO_ON);
5472 ScrollScreen(NULL, SCROLL_GO_ON);
5474 AdvanceFrameAndPlayerCounters(player->index_nr);
5479 Delay(wait_delay_value);
5482 DrawPlayer(player); /* needed here only to cleanup last field */
5483 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5485 player->is_moving = FALSE;
5488 if (IS_CUSTOM_ELEMENT(old_element))
5489 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5491 player->index_bit, leave_side);
5493 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5495 player->index_bit, leave_side);
5497 Feld[jx][jy] = el_player;
5498 InitPlayerField(jx, jy, el_player, TRUE);
5500 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5502 Feld[jx][jy] = element;
5503 InitField(jx, jy, FALSE);
5506 /* only visually relocate centered player */
5507 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5508 FALSE, level.instant_relocation);
5510 TestIfPlayerTouchesBadThing(jx, jy);
5511 TestIfPlayerTouchesCustomElement(jx, jy);
5513 if (IS_CUSTOM_ELEMENT(element))
5514 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5515 player->index_bit, enter_side);
5517 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5518 player->index_bit, enter_side);
5521 void Explode(int ex, int ey, int phase, int mode)
5527 /* !!! eliminate this variable !!! */
5528 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5530 if (game.explosions_delayed)
5532 ExplodeField[ex][ey] = mode;
5536 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5538 int center_element = Feld[ex][ey];
5539 int artwork_element, explosion_element; /* set these values later */
5542 /* --- This is only really needed (and now handled) in "Impact()". --- */
5543 /* do not explode moving elements that left the explode field in time */
5544 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5545 center_element == EL_EMPTY &&
5546 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5551 /* !!! at this place, the center element may be EL_BLOCKED !!! */
5552 if (mode == EX_TYPE_NORMAL ||
5553 mode == EX_TYPE_CENTER ||
5554 mode == EX_TYPE_CROSS)
5555 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5558 /* remove things displayed in background while burning dynamite */
5559 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5562 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5564 /* put moving element to center field (and let it explode there) */
5565 center_element = MovingOrBlocked2Element(ex, ey);
5566 RemoveMovingField(ex, ey);
5567 Feld[ex][ey] = center_element;
5570 /* now "center_element" is finally determined -- set related values now */
5571 artwork_element = center_element; /* for custom player artwork */
5572 explosion_element = center_element; /* for custom player artwork */
5574 if (IS_PLAYER(ex, ey))
5576 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5578 artwork_element = stored_player[player_nr].artwork_element;
5580 if (level.use_explosion_element[player_nr])
5582 explosion_element = level.explosion_element[player_nr];
5583 artwork_element = explosion_element;
5588 if (mode == EX_TYPE_NORMAL ||
5589 mode == EX_TYPE_CENTER ||
5590 mode == EX_TYPE_CROSS)
5591 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5594 last_phase = element_info[explosion_element].explosion_delay + 1;
5596 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5598 int xx = x - ex + 1;
5599 int yy = y - ey + 1;
5602 if (!IN_LEV_FIELD(x, y) ||
5603 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5604 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5607 element = Feld[x][y];
5609 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5611 element = MovingOrBlocked2Element(x, y);
5613 if (!IS_EXPLOSION_PROOF(element))
5614 RemoveMovingField(x, y);
5617 /* indestructible elements can only explode in center (but not flames) */
5618 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5619 mode == EX_TYPE_BORDER)) ||
5620 element == EL_FLAMES)
5623 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5624 behaviour, for example when touching a yamyam that explodes to rocks
5625 with active deadly shield, a rock is created under the player !!! */
5626 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5628 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5629 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5630 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5632 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5635 if (IS_ACTIVE_BOMB(element))
5637 /* re-activate things under the bomb like gate or penguin */
5638 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5645 /* save walkable background elements while explosion on same tile */
5646 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5647 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5648 Back[x][y] = element;
5650 /* ignite explodable elements reached by other explosion */
5651 if (element == EL_EXPLOSION)
5652 element = Store2[x][y];
5654 if (AmoebaNr[x][y] &&
5655 (element == EL_AMOEBA_FULL ||
5656 element == EL_BD_AMOEBA ||
5657 element == EL_AMOEBA_GROWING))
5659 AmoebaCnt[AmoebaNr[x][y]]--;
5660 AmoebaCnt2[AmoebaNr[x][y]]--;
5665 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5667 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5669 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5671 if (PLAYERINFO(ex, ey)->use_murphy)
5672 Store[x][y] = EL_EMPTY;
5675 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5676 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5677 else if (ELEM_IS_PLAYER(center_element))
5678 Store[x][y] = EL_EMPTY;
5679 else if (center_element == EL_YAMYAM)
5680 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5681 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5682 Store[x][y] = element_info[center_element].content.e[xx][yy];
5684 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5685 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5686 otherwise) -- FIX THIS !!! */
5687 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5688 Store[x][y] = element_info[element].content.e[1][1];
5690 else if (!CAN_EXPLODE(element))
5691 Store[x][y] = element_info[element].content.e[1][1];
5694 Store[x][y] = EL_EMPTY;
5696 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5697 center_element == EL_AMOEBA_TO_DIAMOND)
5698 Store2[x][y] = element;
5700 Feld[x][y] = EL_EXPLOSION;
5701 GfxElement[x][y] = artwork_element;
5703 ExplodePhase[x][y] = 1;
5704 ExplodeDelay[x][y] = last_phase;
5709 if (center_element == EL_YAMYAM)
5710 game.yamyam_content_nr =
5711 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5723 GfxFrame[x][y] = 0; /* restart explosion animation */
5725 last_phase = ExplodeDelay[x][y];
5727 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5731 /* activate this even in non-DEBUG version until cause for crash in
5732 getGraphicAnimationFrame() (see below) is found and eliminated */
5738 /* this can happen if the player leaves an explosion just in time */
5739 if (GfxElement[x][y] == EL_UNDEFINED)
5740 GfxElement[x][y] = EL_EMPTY;
5742 if (GfxElement[x][y] == EL_UNDEFINED)
5745 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5746 printf("Explode(): This should never happen!\n");
5749 GfxElement[x][y] = EL_EMPTY;
5755 border_element = Store2[x][y];
5756 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5757 border_element = StorePlayer[x][y];
5759 if (phase == element_info[border_element].ignition_delay ||
5760 phase == last_phase)
5762 boolean border_explosion = FALSE;
5764 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5765 !PLAYER_EXPLOSION_PROTECTED(x, y))
5767 KillPlayerUnlessExplosionProtected(x, y);
5768 border_explosion = TRUE;
5770 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5772 Feld[x][y] = Store2[x][y];
5775 border_explosion = TRUE;
5777 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5779 AmoebeUmwandeln(x, y);
5781 border_explosion = TRUE;
5784 /* if an element just explodes due to another explosion (chain-reaction),
5785 do not immediately end the new explosion when it was the last frame of
5786 the explosion (as it would be done in the following "if"-statement!) */
5787 if (border_explosion && phase == last_phase)
5791 if (phase == last_phase)
5795 element = Feld[x][y] = Store[x][y];
5796 Store[x][y] = Store2[x][y] = 0;
5797 GfxElement[x][y] = EL_UNDEFINED;
5799 /* player can escape from explosions and might therefore be still alive */
5800 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5801 element <= EL_PLAYER_IS_EXPLODING_4)
5803 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5804 int explosion_element = EL_PLAYER_1 + player_nr;
5805 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5806 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5808 if (level.use_explosion_element[player_nr])
5809 explosion_element = level.explosion_element[player_nr];
5811 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5812 element_info[explosion_element].content.e[xx][yy]);
5815 /* restore probably existing indestructible background element */
5816 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5817 element = Feld[x][y] = Back[x][y];
5820 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5821 GfxDir[x][y] = MV_NONE;
5822 ChangeDelay[x][y] = 0;
5823 ChangePage[x][y] = -1;
5825 #if USE_NEW_CUSTOM_VALUE
5826 CustomValue[x][y] = 0;
5829 InitField_WithBug2(x, y, FALSE);
5831 DrawLevelField(x, y);
5833 TestIfElementTouchesCustomElement(x, y);
5835 if (GFX_CRUMBLED(element))
5836 DrawLevelFieldCrumbledSandNeighbours(x, y);
5838 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5839 StorePlayer[x][y] = 0;
5841 if (ELEM_IS_PLAYER(element))
5842 RelocatePlayer(x, y, element);
5844 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5846 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5847 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5850 DrawLevelFieldCrumbledSand(x, y);
5852 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5854 DrawLevelElement(x, y, Back[x][y]);
5855 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5857 else if (IS_WALKABLE_UNDER(Back[x][y]))
5859 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5860 DrawLevelElementThruMask(x, y, Back[x][y]);
5862 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5863 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5867 void DynaExplode(int ex, int ey)
5870 int dynabomb_element = Feld[ex][ey];
5871 int dynabomb_size = 1;
5872 boolean dynabomb_xl = FALSE;
5873 struct PlayerInfo *player;
5874 static int xy[4][2] =
5882 if (IS_ACTIVE_BOMB(dynabomb_element))
5884 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5885 dynabomb_size = player->dynabomb_size;
5886 dynabomb_xl = player->dynabomb_xl;
5887 player->dynabombs_left++;
5890 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5892 for (i = 0; i < NUM_DIRECTIONS; i++)
5894 for (j = 1; j <= dynabomb_size; j++)
5896 int x = ex + j * xy[i][0];
5897 int y = ey + j * xy[i][1];
5900 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5903 element = Feld[x][y];
5905 /* do not restart explosions of fields with active bombs */
5906 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5909 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5911 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5912 !IS_DIGGABLE(element) && !dynabomb_xl)
5918 void Bang(int x, int y)
5920 int element = MovingOrBlocked2Element(x, y);
5921 int explosion_type = EX_TYPE_NORMAL;
5923 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5925 struct PlayerInfo *player = PLAYERINFO(x, y);
5927 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
5928 player->element_nr);
5930 if (level.use_explosion_element[player->index_nr])
5932 int explosion_element = level.explosion_element[player->index_nr];
5934 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5935 explosion_type = EX_TYPE_CROSS;
5936 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5937 explosion_type = EX_TYPE_CENTER;
5945 case EL_BD_BUTTERFLY:
5948 case EL_DARK_YAMYAM:
5952 RaiseScoreElement(element);
5955 case EL_DYNABOMB_PLAYER_1_ACTIVE:
5956 case EL_DYNABOMB_PLAYER_2_ACTIVE:
5957 case EL_DYNABOMB_PLAYER_3_ACTIVE:
5958 case EL_DYNABOMB_PLAYER_4_ACTIVE:
5959 case EL_DYNABOMB_INCREASE_NUMBER:
5960 case EL_DYNABOMB_INCREASE_SIZE:
5961 case EL_DYNABOMB_INCREASE_POWER:
5962 explosion_type = EX_TYPE_DYNA;
5965 case EL_DC_LANDMINE:
5967 case EL_EM_EXIT_OPEN:
5968 case EL_EM_STEEL_EXIT_OPEN:
5970 explosion_type = EX_TYPE_CENTER;
5975 case EL_LAMP_ACTIVE:
5976 case EL_AMOEBA_TO_DIAMOND:
5977 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
5978 explosion_type = EX_TYPE_CENTER;
5982 if (element_info[element].explosion_type == EXPLODES_CROSS)
5983 explosion_type = EX_TYPE_CROSS;
5984 else if (element_info[element].explosion_type == EXPLODES_1X1)
5985 explosion_type = EX_TYPE_CENTER;
5989 if (explosion_type == EX_TYPE_DYNA)
5992 Explode(x, y, EX_PHASE_START, explosion_type);
5994 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5997 void SplashAcid(int x, int y)
5999 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6000 (!IN_LEV_FIELD(x - 1, y - 2) ||
6001 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6002 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6004 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6005 (!IN_LEV_FIELD(x + 1, y - 2) ||
6006 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6007 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6009 PlayLevelSound(x, y, SND_ACID_SPLASHING);
6012 static void InitBeltMovement()
6014 static int belt_base_element[4] =
6016 EL_CONVEYOR_BELT_1_LEFT,
6017 EL_CONVEYOR_BELT_2_LEFT,
6018 EL_CONVEYOR_BELT_3_LEFT,
6019 EL_CONVEYOR_BELT_4_LEFT
6021 static int belt_base_active_element[4] =
6023 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6024 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6025 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6026 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6031 /* set frame order for belt animation graphic according to belt direction */
6032 for (i = 0; i < NUM_BELTS; i++)
6036 for (j = 0; j < NUM_BELT_PARTS; j++)
6038 int element = belt_base_active_element[belt_nr] + j;
6039 int graphic_1 = el2img(element);
6040 int graphic_2 = el2panelimg(element);
6042 if (game.belt_dir[i] == MV_LEFT)
6044 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6045 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6049 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6050 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6055 SCAN_PLAYFIELD(x, y)
6057 int element = Feld[x][y];
6059 for (i = 0; i < NUM_BELTS; i++)
6061 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6063 int e_belt_nr = getBeltNrFromBeltElement(element);
6066 if (e_belt_nr == belt_nr)
6068 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6070 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6077 static void ToggleBeltSwitch(int x, int y)
6079 static int belt_base_element[4] =
6081 EL_CONVEYOR_BELT_1_LEFT,
6082 EL_CONVEYOR_BELT_2_LEFT,
6083 EL_CONVEYOR_BELT_3_LEFT,
6084 EL_CONVEYOR_BELT_4_LEFT
6086 static int belt_base_active_element[4] =
6088 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6089 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6090 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6091 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6093 static int belt_base_switch_element[4] =
6095 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6096 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6097 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6098 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6100 static int belt_move_dir[4] =
6108 int element = Feld[x][y];
6109 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6110 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6111 int belt_dir = belt_move_dir[belt_dir_nr];
6114 if (!IS_BELT_SWITCH(element))
6117 game.belt_dir_nr[belt_nr] = belt_dir_nr;
6118 game.belt_dir[belt_nr] = belt_dir;
6120 if (belt_dir_nr == 3)
6123 /* set frame order for belt animation graphic according to belt direction */
6124 for (i = 0; i < NUM_BELT_PARTS; i++)
6126 int element = belt_base_active_element[belt_nr] + i;
6127 int graphic_1 = el2img(element);
6128 int graphic_2 = el2panelimg(element);
6130 if (belt_dir == MV_LEFT)
6132 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6133 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6137 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6138 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6142 SCAN_PLAYFIELD(xx, yy)
6144 int element = Feld[xx][yy];
6146 if (IS_BELT_SWITCH(element))
6148 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6150 if (e_belt_nr == belt_nr)
6152 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6153 DrawLevelField(xx, yy);
6156 else if (IS_BELT(element) && belt_dir != MV_NONE)
6158 int e_belt_nr = getBeltNrFromBeltElement(element);
6160 if (e_belt_nr == belt_nr)
6162 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6164 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6165 DrawLevelField(xx, yy);
6168 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6170 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6172 if (e_belt_nr == belt_nr)
6174 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6176 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6177 DrawLevelField(xx, yy);
6183 static void ToggleSwitchgateSwitch(int x, int y)
6187 game.switchgate_pos = !game.switchgate_pos;
6189 SCAN_PLAYFIELD(xx, yy)
6191 int element = Feld[xx][yy];
6193 #if !USE_BOTH_SWITCHGATE_SWITCHES
6194 if (element == EL_SWITCHGATE_SWITCH_UP ||
6195 element == EL_SWITCHGATE_SWITCH_DOWN)
6197 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6198 DrawLevelField(xx, yy);
6200 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6201 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6203 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6204 DrawLevelField(xx, yy);
6207 if (element == EL_SWITCHGATE_SWITCH_UP)
6209 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6210 DrawLevelField(xx, yy);
6212 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6214 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6215 DrawLevelField(xx, yy);
6217 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6219 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6220 DrawLevelField(xx, yy);
6222 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6224 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6225 DrawLevelField(xx, yy);
6228 else if (element == EL_SWITCHGATE_OPEN ||
6229 element == EL_SWITCHGATE_OPENING)
6231 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6233 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6235 else if (element == EL_SWITCHGATE_CLOSED ||
6236 element == EL_SWITCHGATE_CLOSING)
6238 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6240 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6245 static int getInvisibleActiveFromInvisibleElement(int element)
6247 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6248 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6249 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6253 static int getInvisibleFromInvisibleActiveElement(int element)
6255 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6256 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6257 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6261 static void RedrawAllLightSwitchesAndInvisibleElements()
6265 SCAN_PLAYFIELD(x, y)
6267 int element = Feld[x][y];
6269 if (element == EL_LIGHT_SWITCH &&
6270 game.light_time_left > 0)
6272 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6273 DrawLevelField(x, y);
6275 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6276 game.light_time_left == 0)
6278 Feld[x][y] = EL_LIGHT_SWITCH;
6279 DrawLevelField(x, y);
6281 else if (element == EL_EMC_DRIPPER &&
6282 game.light_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.light_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.light_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.light_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 RedrawAllInvisibleElementsForLenses()
6326 SCAN_PLAYFIELD(x, y)
6328 int element = Feld[x][y];
6330 if (element == EL_EMC_DRIPPER &&
6331 game.lenses_time_left > 0)
6333 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6334 DrawLevelField(x, y);
6336 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6337 game.lenses_time_left == 0)
6339 Feld[x][y] = EL_EMC_DRIPPER;
6340 DrawLevelField(x, y);
6342 else if (element == EL_INVISIBLE_STEELWALL ||
6343 element == EL_INVISIBLE_WALL ||
6344 element == EL_INVISIBLE_SAND)
6346 if (game.lenses_time_left > 0)
6347 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6349 DrawLevelField(x, y);
6351 /* uncrumble neighbour fields, if needed */
6352 if (element == EL_INVISIBLE_SAND)
6353 DrawLevelFieldCrumbledSandNeighbours(x, y);
6355 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6356 element == EL_INVISIBLE_WALL_ACTIVE ||
6357 element == EL_INVISIBLE_SAND_ACTIVE)
6359 if (game.lenses_time_left == 0)
6360 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6362 DrawLevelField(x, y);
6364 /* re-crumble neighbour fields, if needed */
6365 if (element == EL_INVISIBLE_SAND)
6366 DrawLevelFieldCrumbledSandNeighbours(x, y);
6371 static void RedrawAllInvisibleElementsForMagnifier()
6375 SCAN_PLAYFIELD(x, y)
6377 int element = Feld[x][y];
6379 if (element == EL_EMC_FAKE_GRASS &&
6380 game.magnify_time_left > 0)
6382 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6383 DrawLevelField(x, y);
6385 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6386 game.magnify_time_left == 0)
6388 Feld[x][y] = EL_EMC_FAKE_GRASS;
6389 DrawLevelField(x, y);
6391 else if (IS_GATE_GRAY(element) &&
6392 game.magnify_time_left > 0)
6394 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6395 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6396 IS_EM_GATE_GRAY(element) ?
6397 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6398 IS_EMC_GATE_GRAY(element) ?
6399 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6401 DrawLevelField(x, y);
6403 else if (IS_GATE_GRAY_ACTIVE(element) &&
6404 game.magnify_time_left == 0)
6406 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6407 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6408 IS_EM_GATE_GRAY_ACTIVE(element) ?
6409 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6410 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6411 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6413 DrawLevelField(x, y);
6418 static void ToggleLightSwitch(int x, int y)
6420 int element = Feld[x][y];
6422 game.light_time_left =
6423 (element == EL_LIGHT_SWITCH ?
6424 level.time_light * FRAMES_PER_SECOND : 0);
6426 RedrawAllLightSwitchesAndInvisibleElements();
6429 static void ActivateTimegateSwitch(int x, int y)
6433 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6435 SCAN_PLAYFIELD(xx, yy)
6437 int element = Feld[xx][yy];
6439 if (element == EL_TIMEGATE_CLOSED ||
6440 element == EL_TIMEGATE_CLOSING)
6442 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6443 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6447 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6449 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6450 DrawLevelField(xx, yy);
6457 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6458 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6460 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6464 void Impact(int x, int y)
6466 boolean last_line = (y == lev_fieldy - 1);
6467 boolean object_hit = FALSE;
6468 boolean impact = (last_line || object_hit);
6469 int element = Feld[x][y];
6470 int smashed = EL_STEELWALL;
6472 if (!last_line) /* check if element below was hit */
6474 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6477 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6478 MovDir[x][y + 1] != MV_DOWN ||
6479 MovPos[x][y + 1] <= TILEY / 2));
6481 /* do not smash moving elements that left the smashed field in time */
6482 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6483 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6486 #if USE_QUICKSAND_IMPACT_BUGFIX
6487 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6489 RemoveMovingField(x, y + 1);
6490 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6491 Feld[x][y + 2] = EL_ROCK;
6492 DrawLevelField(x, y + 2);
6497 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6499 RemoveMovingField(x, y + 1);
6500 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6501 Feld[x][y + 2] = EL_ROCK;
6502 DrawLevelField(x, y + 2);
6509 smashed = MovingOrBlocked2Element(x, y + 1);
6511 impact = (last_line || object_hit);
6514 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6516 SplashAcid(x, y + 1);
6520 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6521 /* only reset graphic animation if graphic really changes after impact */
6523 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6525 ResetGfxAnimation(x, y);
6526 DrawLevelField(x, y);
6529 if (impact && CAN_EXPLODE_IMPACT(element))
6534 else if (impact && element == EL_PEARL &&
6535 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6537 ResetGfxAnimation(x, y);
6539 Feld[x][y] = EL_PEARL_BREAKING;
6540 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6543 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6545 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6550 if (impact && element == EL_AMOEBA_DROP)
6552 if (object_hit && IS_PLAYER(x, y + 1))
6553 KillPlayerUnlessEnemyProtected(x, y + 1);
6554 else if (object_hit && smashed == EL_PENGUIN)
6558 Feld[x][y] = EL_AMOEBA_GROWING;
6559 Store[x][y] = EL_AMOEBA_WET;
6561 ResetRandomAnimationValue(x, y);
6566 if (object_hit) /* check which object was hit */
6568 if ((CAN_PASS_MAGIC_WALL(element) &&
6569 (smashed == EL_MAGIC_WALL ||
6570 smashed == EL_BD_MAGIC_WALL)) ||
6571 (CAN_PASS_DC_MAGIC_WALL(element) &&
6572 smashed == EL_DC_MAGIC_WALL))
6575 int activated_magic_wall =
6576 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6577 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6578 EL_DC_MAGIC_WALL_ACTIVE);
6580 /* activate magic wall / mill */
6581 SCAN_PLAYFIELD(xx, yy)
6583 if (Feld[xx][yy] == smashed)
6584 Feld[xx][yy] = activated_magic_wall;
6587 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6588 game.magic_wall_active = TRUE;
6590 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6591 SND_MAGIC_WALL_ACTIVATING :
6592 smashed == EL_BD_MAGIC_WALL ?
6593 SND_BD_MAGIC_WALL_ACTIVATING :
6594 SND_DC_MAGIC_WALL_ACTIVATING));
6597 if (IS_PLAYER(x, y + 1))
6599 if (CAN_SMASH_PLAYER(element))
6601 KillPlayerUnlessEnemyProtected(x, y + 1);
6605 else if (smashed == EL_PENGUIN)
6607 if (CAN_SMASH_PLAYER(element))
6613 else if (element == EL_BD_DIAMOND)
6615 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6621 else if (((element == EL_SP_INFOTRON ||
6622 element == EL_SP_ZONK) &&
6623 (smashed == EL_SP_SNIKSNAK ||
6624 smashed == EL_SP_ELECTRON ||
6625 smashed == EL_SP_DISK_ORANGE)) ||
6626 (element == EL_SP_INFOTRON &&
6627 smashed == EL_SP_DISK_YELLOW))
6632 else if (CAN_SMASH_EVERYTHING(element))
6634 if (IS_CLASSIC_ENEMY(smashed) ||
6635 CAN_EXPLODE_SMASHED(smashed))
6640 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6642 if (smashed == EL_LAMP ||
6643 smashed == EL_LAMP_ACTIVE)
6648 else if (smashed == EL_NUT)
6650 Feld[x][y + 1] = EL_NUT_BREAKING;
6651 PlayLevelSound(x, y, SND_NUT_BREAKING);
6652 RaiseScoreElement(EL_NUT);
6655 else if (smashed == EL_PEARL)
6657 ResetGfxAnimation(x, y);
6659 Feld[x][y + 1] = EL_PEARL_BREAKING;
6660 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6663 else if (smashed == EL_DIAMOND)
6665 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6666 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6669 else if (IS_BELT_SWITCH(smashed))
6671 ToggleBeltSwitch(x, y + 1);
6673 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6674 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6675 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6676 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6678 ToggleSwitchgateSwitch(x, y + 1);
6680 else if (smashed == EL_LIGHT_SWITCH ||
6681 smashed == EL_LIGHT_SWITCH_ACTIVE)
6683 ToggleLightSwitch(x, y + 1);
6688 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6691 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6693 CheckElementChangeBySide(x, y + 1, smashed, element,
6694 CE_SWITCHED, CH_SIDE_TOP);
6695 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6701 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6706 /* play sound of magic wall / mill */
6708 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6709 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6710 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6712 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6713 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6714 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6715 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6716 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6717 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6722 /* play sound of object that hits the ground */
6723 if (last_line || object_hit)
6724 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6727 inline static void TurnRoundExt(int x, int y)
6739 { 0, 0 }, { 0, 0 }, { 0, 0 },
6744 int left, right, back;
6748 { MV_DOWN, MV_UP, MV_RIGHT },
6749 { MV_UP, MV_DOWN, MV_LEFT },
6751 { MV_LEFT, MV_RIGHT, MV_DOWN },
6755 { MV_RIGHT, MV_LEFT, MV_UP }
6758 int element = Feld[x][y];
6759 int move_pattern = element_info[element].move_pattern;
6761 int old_move_dir = MovDir[x][y];
6762 int left_dir = turn[old_move_dir].left;
6763 int right_dir = turn[old_move_dir].right;
6764 int back_dir = turn[old_move_dir].back;
6766 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6767 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6768 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6769 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6771 int left_x = x + left_dx, left_y = y + left_dy;
6772 int right_x = x + right_dx, right_y = y + right_dy;
6773 int move_x = x + move_dx, move_y = y + move_dy;
6777 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6779 TestIfBadThingTouchesOtherBadThing(x, y);
6781 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6782 MovDir[x][y] = right_dir;
6783 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6784 MovDir[x][y] = left_dir;
6786 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6788 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6791 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6793 TestIfBadThingTouchesOtherBadThing(x, y);
6795 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6796 MovDir[x][y] = left_dir;
6797 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6798 MovDir[x][y] = right_dir;
6800 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6802 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6805 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6807 TestIfBadThingTouchesOtherBadThing(x, y);
6809 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6810 MovDir[x][y] = left_dir;
6811 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6812 MovDir[x][y] = right_dir;
6814 if (MovDir[x][y] != old_move_dir)
6817 else if (element == EL_YAMYAM)
6819 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6820 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6822 if (can_turn_left && can_turn_right)
6823 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6824 else if (can_turn_left)
6825 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6826 else if (can_turn_right)
6827 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6829 MovDir[x][y] = back_dir;
6831 MovDelay[x][y] = 16 + 16 * RND(3);
6833 else if (element == EL_DARK_YAMYAM)
6835 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6837 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6840 if (can_turn_left && can_turn_right)
6841 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6842 else if (can_turn_left)
6843 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6844 else if (can_turn_right)
6845 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6847 MovDir[x][y] = back_dir;
6849 MovDelay[x][y] = 16 + 16 * RND(3);
6851 else if (element == EL_PACMAN)
6853 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6854 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6856 if (can_turn_left && can_turn_right)
6857 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6858 else if (can_turn_left)
6859 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6860 else if (can_turn_right)
6861 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6863 MovDir[x][y] = back_dir;
6865 MovDelay[x][y] = 6 + RND(40);
6867 else if (element == EL_PIG)
6869 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6870 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6871 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6872 boolean should_turn_left, should_turn_right, should_move_on;
6874 int rnd = RND(rnd_value);
6876 should_turn_left = (can_turn_left &&
6878 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6879 y + back_dy + left_dy)));
6880 should_turn_right = (can_turn_right &&
6882 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6883 y + back_dy + right_dy)));
6884 should_move_on = (can_move_on &&
6887 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6888 y + move_dy + left_dy) ||
6889 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6890 y + move_dy + right_dy)));
6892 if (should_turn_left || should_turn_right || should_move_on)
6894 if (should_turn_left && should_turn_right && should_move_on)
6895 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6896 rnd < 2 * rnd_value / 3 ? right_dir :
6898 else if (should_turn_left && should_turn_right)
6899 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6900 else if (should_turn_left && should_move_on)
6901 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6902 else if (should_turn_right && should_move_on)
6903 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6904 else if (should_turn_left)
6905 MovDir[x][y] = left_dir;
6906 else if (should_turn_right)
6907 MovDir[x][y] = right_dir;
6908 else if (should_move_on)
6909 MovDir[x][y] = old_move_dir;
6911 else if (can_move_on && rnd > rnd_value / 8)
6912 MovDir[x][y] = old_move_dir;
6913 else if (can_turn_left && can_turn_right)
6914 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6915 else if (can_turn_left && rnd > rnd_value / 8)
6916 MovDir[x][y] = left_dir;
6917 else if (can_turn_right && rnd > rnd_value/8)
6918 MovDir[x][y] = right_dir;
6920 MovDir[x][y] = back_dir;
6922 xx = x + move_xy[MovDir[x][y]].dx;
6923 yy = y + move_xy[MovDir[x][y]].dy;
6925 if (!IN_LEV_FIELD(xx, yy) ||
6926 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6927 MovDir[x][y] = old_move_dir;
6931 else if (element == EL_DRAGON)
6933 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6934 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6935 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6937 int rnd = RND(rnd_value);
6939 if (can_move_on && rnd > rnd_value / 8)
6940 MovDir[x][y] = old_move_dir;
6941 else if (can_turn_left && can_turn_right)
6942 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6943 else if (can_turn_left && rnd > rnd_value / 8)
6944 MovDir[x][y] = left_dir;
6945 else if (can_turn_right && rnd > rnd_value / 8)
6946 MovDir[x][y] = right_dir;
6948 MovDir[x][y] = back_dir;
6950 xx = x + move_xy[MovDir[x][y]].dx;
6951 yy = y + move_xy[MovDir[x][y]].dy;
6953 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6954 MovDir[x][y] = old_move_dir;
6958 else if (element == EL_MOLE)
6960 boolean can_move_on =
6961 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6962 IS_AMOEBOID(Feld[move_x][move_y]) ||
6963 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6966 boolean can_turn_left =
6967 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6968 IS_AMOEBOID(Feld[left_x][left_y])));
6970 boolean can_turn_right =
6971 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6972 IS_AMOEBOID(Feld[right_x][right_y])));
6974 if (can_turn_left && can_turn_right)
6975 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6976 else if (can_turn_left)
6977 MovDir[x][y] = left_dir;
6979 MovDir[x][y] = right_dir;
6982 if (MovDir[x][y] != old_move_dir)
6985 else if (element == EL_BALLOON)
6987 MovDir[x][y] = game.wind_direction;
6990 else if (element == EL_SPRING)
6992 #if USE_NEW_SPRING_BUMPER
6993 if (MovDir[x][y] & MV_HORIZONTAL)
6995 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6996 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6998 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6999 ResetGfxAnimation(move_x, move_y);
7000 DrawLevelField(move_x, move_y);
7002 MovDir[x][y] = back_dir;
7004 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7005 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7006 MovDir[x][y] = MV_NONE;
7009 if (MovDir[x][y] & MV_HORIZONTAL &&
7010 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7011 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7012 MovDir[x][y] = MV_NONE;
7017 else if (element == EL_ROBOT ||
7018 element == EL_SATELLITE ||
7019 element == EL_PENGUIN ||
7020 element == EL_EMC_ANDROID)
7022 int attr_x = -1, attr_y = -1;
7033 for (i = 0; i < MAX_PLAYERS; i++)
7035 struct PlayerInfo *player = &stored_player[i];
7036 int jx = player->jx, jy = player->jy;
7038 if (!player->active)
7042 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7050 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7051 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7052 game.engine_version < VERSION_IDENT(3,1,0,0)))
7058 if (element == EL_PENGUIN)
7061 static int xy[4][2] =
7069 for (i = 0; i < NUM_DIRECTIONS; i++)
7071 int ex = x + xy[i][0];
7072 int ey = y + xy[i][1];
7074 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7075 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7076 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7077 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7086 MovDir[x][y] = MV_NONE;
7088 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7089 else if (attr_x > x)
7090 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7092 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7093 else if (attr_y > y)
7094 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7096 if (element == EL_ROBOT)
7100 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7101 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7102 Moving2Blocked(x, y, &newx, &newy);
7104 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7105 MovDelay[x][y] = 8 + 8 * !RND(3);
7107 MovDelay[x][y] = 16;
7109 else if (element == EL_PENGUIN)
7115 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7117 boolean first_horiz = RND(2);
7118 int new_move_dir = MovDir[x][y];
7121 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7122 Moving2Blocked(x, y, &newx, &newy);
7124 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7128 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7129 Moving2Blocked(x, y, &newx, &newy);
7131 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7134 MovDir[x][y] = old_move_dir;
7138 else if (element == EL_SATELLITE)
7144 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7146 boolean first_horiz = RND(2);
7147 int new_move_dir = MovDir[x][y];
7150 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7151 Moving2Blocked(x, y, &newx, &newy);
7153 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7157 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7158 Moving2Blocked(x, y, &newx, &newy);
7160 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7163 MovDir[x][y] = old_move_dir;
7167 else if (element == EL_EMC_ANDROID)
7169 static int check_pos[16] =
7171 -1, /* 0 => (invalid) */
7172 7, /* 1 => MV_LEFT */
7173 3, /* 2 => MV_RIGHT */
7174 -1, /* 3 => (invalid) */
7176 0, /* 5 => MV_LEFT | MV_UP */
7177 2, /* 6 => MV_RIGHT | MV_UP */
7178 -1, /* 7 => (invalid) */
7179 5, /* 8 => MV_DOWN */
7180 6, /* 9 => MV_LEFT | MV_DOWN */
7181 4, /* 10 => MV_RIGHT | MV_DOWN */
7182 -1, /* 11 => (invalid) */
7183 -1, /* 12 => (invalid) */
7184 -1, /* 13 => (invalid) */
7185 -1, /* 14 => (invalid) */
7186 -1, /* 15 => (invalid) */
7194 { -1, -1, MV_LEFT | MV_UP },
7196 { +1, -1, MV_RIGHT | MV_UP },
7197 { +1, 0, MV_RIGHT },
7198 { +1, +1, MV_RIGHT | MV_DOWN },
7200 { -1, +1, MV_LEFT | MV_DOWN },
7203 int start_pos, check_order;
7204 boolean can_clone = FALSE;
7207 /* check if there is any free field around current position */
7208 for (i = 0; i < 8; i++)
7210 int newx = x + check_xy[i].dx;
7211 int newy = y + check_xy[i].dy;
7213 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7221 if (can_clone) /* randomly find an element to clone */
7225 start_pos = check_pos[RND(8)];
7226 check_order = (RND(2) ? -1 : +1);
7228 for (i = 0; i < 8; i++)
7230 int pos_raw = start_pos + i * check_order;
7231 int pos = (pos_raw + 8) % 8;
7232 int newx = x + check_xy[pos].dx;
7233 int newy = y + check_xy[pos].dy;
7235 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7237 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7238 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7240 Store[x][y] = Feld[newx][newy];
7249 if (can_clone) /* randomly find a direction to move */
7253 start_pos = check_pos[RND(8)];
7254 check_order = (RND(2) ? -1 : +1);
7256 for (i = 0; i < 8; i++)
7258 int pos_raw = start_pos + i * check_order;
7259 int pos = (pos_raw + 8) % 8;
7260 int newx = x + check_xy[pos].dx;
7261 int newy = y + check_xy[pos].dy;
7262 int new_move_dir = check_xy[pos].dir;
7264 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7266 MovDir[x][y] = new_move_dir;
7267 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7276 if (can_clone) /* cloning and moving successful */
7279 /* cannot clone -- try to move towards player */
7281 start_pos = check_pos[MovDir[x][y] & 0x0f];
7282 check_order = (RND(2) ? -1 : +1);
7284 for (i = 0; i < 3; i++)
7286 /* first check start_pos, then previous/next or (next/previous) pos */
7287 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7288 int pos = (pos_raw + 8) % 8;
7289 int newx = x + check_xy[pos].dx;
7290 int newy = y + check_xy[pos].dy;
7291 int new_move_dir = check_xy[pos].dir;
7293 if (IS_PLAYER(newx, newy))
7296 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7298 MovDir[x][y] = new_move_dir;
7299 MovDelay[x][y] = level.android_move_time * 8 + 1;
7306 else if (move_pattern == MV_TURNING_LEFT ||
7307 move_pattern == MV_TURNING_RIGHT ||
7308 move_pattern == MV_TURNING_LEFT_RIGHT ||
7309 move_pattern == MV_TURNING_RIGHT_LEFT ||
7310 move_pattern == MV_TURNING_RANDOM ||
7311 move_pattern == MV_ALL_DIRECTIONS)
7313 boolean can_turn_left =
7314 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7315 boolean can_turn_right =
7316 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7318 if (element_info[element].move_stepsize == 0) /* "not moving" */
7321 if (move_pattern == MV_TURNING_LEFT)
7322 MovDir[x][y] = left_dir;
7323 else if (move_pattern == MV_TURNING_RIGHT)
7324 MovDir[x][y] = right_dir;
7325 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7326 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7327 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7328 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7329 else if (move_pattern == MV_TURNING_RANDOM)
7330 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7331 can_turn_right && !can_turn_left ? right_dir :
7332 RND(2) ? left_dir : right_dir);
7333 else if (can_turn_left && can_turn_right)
7334 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7335 else if (can_turn_left)
7336 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7337 else if (can_turn_right)
7338 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7340 MovDir[x][y] = back_dir;
7342 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7344 else if (move_pattern == MV_HORIZONTAL ||
7345 move_pattern == MV_VERTICAL)
7347 if (move_pattern & old_move_dir)
7348 MovDir[x][y] = back_dir;
7349 else if (move_pattern == MV_HORIZONTAL)
7350 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7351 else if (move_pattern == MV_VERTICAL)
7352 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7354 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7356 else if (move_pattern & MV_ANY_DIRECTION)
7358 MovDir[x][y] = move_pattern;
7359 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7361 else if (move_pattern & MV_WIND_DIRECTION)
7363 MovDir[x][y] = game.wind_direction;
7364 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7366 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7368 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7369 MovDir[x][y] = left_dir;
7370 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7371 MovDir[x][y] = right_dir;
7373 if (MovDir[x][y] != old_move_dir)
7374 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7376 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7378 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7379 MovDir[x][y] = right_dir;
7380 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7381 MovDir[x][y] = left_dir;
7383 if (MovDir[x][y] != old_move_dir)
7384 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7386 else if (move_pattern == MV_TOWARDS_PLAYER ||
7387 move_pattern == MV_AWAY_FROM_PLAYER)
7389 int attr_x = -1, attr_y = -1;
7391 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7402 for (i = 0; i < MAX_PLAYERS; i++)
7404 struct PlayerInfo *player = &stored_player[i];
7405 int jx = player->jx, jy = player->jy;
7407 if (!player->active)
7411 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7419 MovDir[x][y] = MV_NONE;
7421 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7422 else if (attr_x > x)
7423 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7425 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7426 else if (attr_y > y)
7427 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7429 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7431 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7433 boolean first_horiz = RND(2);
7434 int new_move_dir = MovDir[x][y];
7436 if (element_info[element].move_stepsize == 0) /* "not moving" */
7438 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7439 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7445 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7446 Moving2Blocked(x, y, &newx, &newy);
7448 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7452 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7453 Moving2Blocked(x, y, &newx, &newy);
7455 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7458 MovDir[x][y] = old_move_dir;
7461 else if (move_pattern == MV_WHEN_PUSHED ||
7462 move_pattern == MV_WHEN_DROPPED)
7464 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7465 MovDir[x][y] = MV_NONE;
7469 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7471 static int test_xy[7][2] =
7481 static int test_dir[7] =
7491 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7492 int move_preference = -1000000; /* start with very low preference */
7493 int new_move_dir = MV_NONE;
7494 int start_test = RND(4);
7497 for (i = 0; i < NUM_DIRECTIONS; i++)
7499 int move_dir = test_dir[start_test + i];
7500 int move_dir_preference;
7502 xx = x + test_xy[start_test + i][0];
7503 yy = y + test_xy[start_test + i][1];
7505 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7506 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7508 new_move_dir = move_dir;
7513 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7516 move_dir_preference = -1 * RunnerVisit[xx][yy];
7517 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7518 move_dir_preference = PlayerVisit[xx][yy];
7520 if (move_dir_preference > move_preference)
7522 /* prefer field that has not been visited for the longest time */
7523 move_preference = move_dir_preference;
7524 new_move_dir = move_dir;
7526 else if (move_dir_preference == move_preference &&
7527 move_dir == old_move_dir)
7529 /* prefer last direction when all directions are preferred equally */
7530 move_preference = move_dir_preference;
7531 new_move_dir = move_dir;
7535 MovDir[x][y] = new_move_dir;
7536 if (old_move_dir != new_move_dir)
7537 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7541 static void TurnRound(int x, int y)
7543 int direction = MovDir[x][y];
7547 GfxDir[x][y] = MovDir[x][y];
7549 if (direction != MovDir[x][y])
7553 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7555 ResetGfxFrame(x, y, FALSE);
7558 static boolean JustBeingPushed(int x, int y)
7562 for (i = 0; i < MAX_PLAYERS; i++)
7564 struct PlayerInfo *player = &stored_player[i];
7566 if (player->active && player->is_pushing && player->MovPos)
7568 int next_jx = player->jx + (player->jx - player->last_jx);
7569 int next_jy = player->jy + (player->jy - player->last_jy);
7571 if (x == next_jx && y == next_jy)
7579 void StartMoving(int x, int y)
7581 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7582 int element = Feld[x][y];
7587 if (MovDelay[x][y] == 0)
7588 GfxAction[x][y] = ACTION_DEFAULT;
7590 if (CAN_FALL(element) && y < lev_fieldy - 1)
7592 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7593 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7594 if (JustBeingPushed(x, y))
7597 if (element == EL_QUICKSAND_FULL)
7599 if (IS_FREE(x, y + 1))
7601 InitMovingField(x, y, MV_DOWN);
7602 started_moving = TRUE;
7604 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7605 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7606 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7607 Store[x][y] = EL_ROCK;
7609 Store[x][y] = EL_ROCK;
7612 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7614 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7616 if (!MovDelay[x][y])
7618 MovDelay[x][y] = TILEY + 1;
7620 ResetGfxAnimation(x, y);
7621 ResetGfxAnimation(x, y + 1);
7626 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7627 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7634 Feld[x][y] = EL_QUICKSAND_EMPTY;
7635 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7636 Store[x][y + 1] = Store[x][y];
7639 PlayLevelSoundAction(x, y, ACTION_FILLING);
7641 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7643 if (!MovDelay[x][y])
7645 MovDelay[x][y] = TILEY + 1;
7647 ResetGfxAnimation(x, y);
7648 ResetGfxAnimation(x, y + 1);
7653 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7654 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7661 Feld[x][y] = EL_QUICKSAND_EMPTY;
7662 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7663 Store[x][y + 1] = Store[x][y];
7666 PlayLevelSoundAction(x, y, ACTION_FILLING);
7669 else if (element == EL_QUICKSAND_FAST_FULL)
7671 if (IS_FREE(x, y + 1))
7673 InitMovingField(x, y, MV_DOWN);
7674 started_moving = TRUE;
7676 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7677 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7678 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7679 Store[x][y] = EL_ROCK;
7681 Store[x][y] = EL_ROCK;
7684 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7686 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7688 if (!MovDelay[x][y])
7690 MovDelay[x][y] = TILEY + 1;
7692 ResetGfxAnimation(x, y);
7693 ResetGfxAnimation(x, y + 1);
7698 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7699 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7706 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7707 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7708 Store[x][y + 1] = Store[x][y];
7711 PlayLevelSoundAction(x, y, ACTION_FILLING);
7713 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7715 if (!MovDelay[x][y])
7717 MovDelay[x][y] = TILEY + 1;
7719 ResetGfxAnimation(x, y);
7720 ResetGfxAnimation(x, y + 1);
7725 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7726 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7733 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7734 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7735 Store[x][y + 1] = Store[x][y];
7738 PlayLevelSoundAction(x, y, ACTION_FILLING);
7741 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7742 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7744 InitMovingField(x, y, MV_DOWN);
7745 started_moving = TRUE;
7747 Feld[x][y] = EL_QUICKSAND_FILLING;
7748 Store[x][y] = element;
7750 PlayLevelSoundAction(x, y, ACTION_FILLING);
7752 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7753 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7755 InitMovingField(x, y, MV_DOWN);
7756 started_moving = TRUE;
7758 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7759 Store[x][y] = element;
7761 PlayLevelSoundAction(x, y, ACTION_FILLING);
7763 else if (element == EL_MAGIC_WALL_FULL)
7765 if (IS_FREE(x, y + 1))
7767 InitMovingField(x, y, MV_DOWN);
7768 started_moving = TRUE;
7770 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7771 Store[x][y] = EL_CHANGED(Store[x][y]);
7773 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7775 if (!MovDelay[x][y])
7776 MovDelay[x][y] = TILEY/4 + 1;
7785 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7786 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7787 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7791 else if (element == EL_BD_MAGIC_WALL_FULL)
7793 if (IS_FREE(x, y + 1))
7795 InitMovingField(x, y, MV_DOWN);
7796 started_moving = TRUE;
7798 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7799 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7801 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7803 if (!MovDelay[x][y])
7804 MovDelay[x][y] = TILEY/4 + 1;
7813 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7814 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7815 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7819 else if (element == EL_DC_MAGIC_WALL_FULL)
7821 if (IS_FREE(x, y + 1))
7823 InitMovingField(x, y, MV_DOWN);
7824 started_moving = TRUE;
7826 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7827 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7829 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7831 if (!MovDelay[x][y])
7832 MovDelay[x][y] = TILEY/4 + 1;
7841 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7842 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7843 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7847 else if ((CAN_PASS_MAGIC_WALL(element) &&
7848 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7849 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7850 (CAN_PASS_DC_MAGIC_WALL(element) &&
7851 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7854 InitMovingField(x, y, MV_DOWN);
7855 started_moving = TRUE;
7858 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7859 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7860 EL_DC_MAGIC_WALL_FILLING);
7861 Store[x][y] = element;
7863 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7865 SplashAcid(x, y + 1);
7867 InitMovingField(x, y, MV_DOWN);
7868 started_moving = TRUE;
7870 Store[x][y] = EL_ACID;
7873 #if USE_FIX_IMPACT_COLLISION
7874 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7875 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7877 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7878 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7880 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7881 CAN_FALL(element) && WasJustFalling[x][y] &&
7882 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7884 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7885 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7886 (Feld[x][y + 1] == EL_BLOCKED)))
7888 /* this is needed for a special case not covered by calling "Impact()"
7889 from "ContinueMoving()": if an element moves to a tile directly below
7890 another element which was just falling on that tile (which was empty
7891 in the previous frame), the falling element above would just stop
7892 instead of smashing the element below (in previous version, the above
7893 element was just checked for "moving" instead of "falling", resulting
7894 in incorrect smashes caused by horizontal movement of the above
7895 element; also, the case of the player being the element to smash was
7896 simply not covered here... :-/ ) */
7898 CheckCollision[x][y] = 0;
7899 CheckImpact[x][y] = 0;
7903 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7905 if (MovDir[x][y] == MV_NONE)
7907 InitMovingField(x, y, MV_DOWN);
7908 started_moving = TRUE;
7911 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7913 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7914 MovDir[x][y] = MV_DOWN;
7916 InitMovingField(x, y, MV_DOWN);
7917 started_moving = TRUE;
7919 else if (element == EL_AMOEBA_DROP)
7921 Feld[x][y] = EL_AMOEBA_GROWING;
7922 Store[x][y] = EL_AMOEBA_WET;
7924 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7925 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7926 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7927 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7929 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
7930 (IS_FREE(x - 1, y + 1) ||
7931 Feld[x - 1][y + 1] == EL_ACID));
7932 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7933 (IS_FREE(x + 1, y + 1) ||
7934 Feld[x + 1][y + 1] == EL_ACID));
7935 boolean can_fall_any = (can_fall_left || can_fall_right);
7936 boolean can_fall_both = (can_fall_left && can_fall_right);
7937 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7939 #if USE_NEW_ALL_SLIPPERY
7940 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7942 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7943 can_fall_right = FALSE;
7944 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7945 can_fall_left = FALSE;
7946 else if (slippery_type == SLIPPERY_ONLY_LEFT)
7947 can_fall_right = FALSE;
7948 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7949 can_fall_left = FALSE;
7951 can_fall_any = (can_fall_left || can_fall_right);
7952 can_fall_both = FALSE;
7955 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
7957 if (slippery_type == SLIPPERY_ONLY_LEFT)
7958 can_fall_right = FALSE;
7959 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7960 can_fall_left = FALSE;
7961 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7962 can_fall_right = FALSE;
7963 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7964 can_fall_left = FALSE;
7966 can_fall_any = (can_fall_left || can_fall_right);
7967 can_fall_both = (can_fall_left && can_fall_right);
7971 #if USE_NEW_ALL_SLIPPERY
7973 #if USE_NEW_SP_SLIPPERY
7974 /* !!! better use the same properties as for custom elements here !!! */
7975 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
7976 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
7978 can_fall_right = FALSE; /* slip down on left side */
7979 can_fall_both = FALSE;
7984 #if USE_NEW_ALL_SLIPPERY
7987 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7988 can_fall_right = FALSE; /* slip down on left side */
7990 can_fall_left = !(can_fall_right = RND(2));
7992 can_fall_both = FALSE;
7997 if (game.emulation == EMU_BOULDERDASH ||
7998 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7999 can_fall_right = FALSE; /* slip down on left side */
8001 can_fall_left = !(can_fall_right = RND(2));
8003 can_fall_both = FALSE;
8009 /* if not determined otherwise, prefer left side for slipping down */
8010 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8011 started_moving = TRUE;
8015 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8017 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8020 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
8021 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8022 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8023 int belt_dir = game.belt_dir[belt_nr];
8025 if ((belt_dir == MV_LEFT && left_is_free) ||
8026 (belt_dir == MV_RIGHT && right_is_free))
8028 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8030 InitMovingField(x, y, belt_dir);
8031 started_moving = TRUE;
8033 Pushed[x][y] = TRUE;
8034 Pushed[nextx][y] = TRUE;
8036 GfxAction[x][y] = ACTION_DEFAULT;
8040 MovDir[x][y] = 0; /* if element was moving, stop it */
8045 /* not "else if" because of elements that can fall and move (EL_SPRING) */
8047 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8049 if (CAN_MOVE(element) && !started_moving)
8052 int move_pattern = element_info[element].move_pattern;
8057 if (MovDir[x][y] == MV_NONE)
8059 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8060 x, y, element, element_info[element].token_name);
8061 printf("StartMoving(): This should never happen!\n");
8066 Moving2Blocked(x, y, &newx, &newy);
8068 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8071 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8072 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8074 WasJustMoving[x][y] = 0;
8075 CheckCollision[x][y] = 0;
8077 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8079 if (Feld[x][y] != element) /* element has changed */
8083 if (!MovDelay[x][y]) /* start new movement phase */
8085 /* all objects that can change their move direction after each step
8086 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8088 if (element != EL_YAMYAM &&
8089 element != EL_DARK_YAMYAM &&
8090 element != EL_PACMAN &&
8091 !(move_pattern & MV_ANY_DIRECTION) &&
8092 move_pattern != MV_TURNING_LEFT &&
8093 move_pattern != MV_TURNING_RIGHT &&
8094 move_pattern != MV_TURNING_LEFT_RIGHT &&
8095 move_pattern != MV_TURNING_RIGHT_LEFT &&
8096 move_pattern != MV_TURNING_RANDOM)
8100 if (MovDelay[x][y] && (element == EL_BUG ||
8101 element == EL_SPACESHIP ||
8102 element == EL_SP_SNIKSNAK ||
8103 element == EL_SP_ELECTRON ||
8104 element == EL_MOLE))
8105 DrawLevelField(x, y);
8109 if (MovDelay[x][y]) /* wait some time before next movement */
8113 if (element == EL_ROBOT ||
8114 element == EL_YAMYAM ||
8115 element == EL_DARK_YAMYAM)
8117 DrawLevelElementAnimationIfNeeded(x, y, element);
8118 PlayLevelSoundAction(x, y, ACTION_WAITING);
8120 else if (element == EL_SP_ELECTRON)
8121 DrawLevelElementAnimationIfNeeded(x, y, element);
8122 else if (element == EL_DRAGON)
8125 int dir = MovDir[x][y];
8126 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8127 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
8128 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
8129 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
8130 dir == MV_UP ? IMG_FLAMES_1_UP :
8131 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8132 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8134 GfxAction[x][y] = ACTION_ATTACKING;
8136 if (IS_PLAYER(x, y))
8137 DrawPlayerField(x, y);
8139 DrawLevelField(x, y);
8141 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8143 for (i = 1; i <= 3; i++)
8145 int xx = x + i * dx;
8146 int yy = y + i * dy;
8147 int sx = SCREENX(xx);
8148 int sy = SCREENY(yy);
8149 int flame_graphic = graphic + (i - 1);
8151 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8156 int flamed = MovingOrBlocked2Element(xx, yy);
8160 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8162 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8163 RemoveMovingField(xx, yy);
8165 RemoveField(xx, yy);
8167 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8170 RemoveMovingField(xx, yy);
8173 ChangeDelay[xx][yy] = 0;
8175 Feld[xx][yy] = EL_FLAMES;
8177 if (IN_SCR_FIELD(sx, sy))
8179 DrawLevelFieldCrumbledSand(xx, yy);
8180 DrawGraphic(sx, sy, flame_graphic, frame);
8185 if (Feld[xx][yy] == EL_FLAMES)
8186 Feld[xx][yy] = EL_EMPTY;
8187 DrawLevelField(xx, yy);
8192 if (MovDelay[x][y]) /* element still has to wait some time */
8194 PlayLevelSoundAction(x, y, ACTION_WAITING);
8200 /* now make next step */
8202 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8204 if (DONT_COLLIDE_WITH(element) &&
8205 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8206 !PLAYER_ENEMY_PROTECTED(newx, newy))
8208 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8213 else if (CAN_MOVE_INTO_ACID(element) &&
8214 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8215 !IS_MV_DIAGONAL(MovDir[x][y]) &&
8216 (MovDir[x][y] == MV_DOWN ||
8217 game.engine_version >= VERSION_IDENT(3,1,0,0)))
8219 SplashAcid(newx, newy);
8220 Store[x][y] = EL_ACID;
8222 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8224 if (Feld[newx][newy] == EL_EXIT_OPEN ||
8225 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8226 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8227 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8230 DrawLevelField(x, y);
8232 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8233 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8234 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8236 local_player->friends_still_needed--;
8237 if (!local_player->friends_still_needed &&
8238 !local_player->GameOver && AllPlayersGone)
8239 PlayerWins(local_player);
8243 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8245 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8246 DrawLevelField(newx, newy);
8248 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8250 else if (!IS_FREE(newx, newy))
8252 GfxAction[x][y] = ACTION_WAITING;
8254 if (IS_PLAYER(x, y))
8255 DrawPlayerField(x, y);
8257 DrawLevelField(x, y);
8262 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8264 if (IS_FOOD_PIG(Feld[newx][newy]))
8266 if (IS_MOVING(newx, newy))
8267 RemoveMovingField(newx, newy);
8270 Feld[newx][newy] = EL_EMPTY;
8271 DrawLevelField(newx, newy);
8274 PlayLevelSound(x, y, SND_PIG_DIGGING);
8276 else if (!IS_FREE(newx, newy))
8278 if (IS_PLAYER(x, y))
8279 DrawPlayerField(x, y);
8281 DrawLevelField(x, y);
8286 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8288 if (Store[x][y] != EL_EMPTY)
8290 boolean can_clone = FALSE;
8293 /* check if element to clone is still there */
8294 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8296 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8304 /* cannot clone or target field not free anymore -- do not clone */
8305 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8306 Store[x][y] = EL_EMPTY;
8309 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8311 if (IS_MV_DIAGONAL(MovDir[x][y]))
8313 int diagonal_move_dir = MovDir[x][y];
8314 int stored = Store[x][y];
8315 int change_delay = 8;
8318 /* android is moving diagonally */
8320 CreateField(x, y, EL_DIAGONAL_SHRINKING);
8322 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8323 GfxElement[x][y] = EL_EMC_ANDROID;
8324 GfxAction[x][y] = ACTION_SHRINKING;
8325 GfxDir[x][y] = diagonal_move_dir;
8326 ChangeDelay[x][y] = change_delay;
8328 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8331 DrawLevelGraphicAnimation(x, y, graphic);
8332 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8334 if (Feld[newx][newy] == EL_ACID)
8336 SplashAcid(newx, newy);
8341 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8343 Store[newx][newy] = EL_EMC_ANDROID;
8344 GfxElement[newx][newy] = EL_EMC_ANDROID;
8345 GfxAction[newx][newy] = ACTION_GROWING;
8346 GfxDir[newx][newy] = diagonal_move_dir;
8347 ChangeDelay[newx][newy] = change_delay;
8349 graphic = el_act_dir2img(GfxElement[newx][newy],
8350 GfxAction[newx][newy], GfxDir[newx][newy]);
8352 DrawLevelGraphicAnimation(newx, newy, graphic);
8353 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8359 Feld[newx][newy] = EL_EMPTY;
8360 DrawLevelField(newx, newy);
8362 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8365 else if (!IS_FREE(newx, newy))
8368 if (IS_PLAYER(x, y))
8369 DrawPlayerField(x, y);
8371 DrawLevelField(x, y);
8377 else if (IS_CUSTOM_ELEMENT(element) &&
8378 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8380 int new_element = Feld[newx][newy];
8382 if (!IS_FREE(newx, newy))
8384 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8385 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8388 /* no element can dig solid indestructible elements */
8389 if (IS_INDESTRUCTIBLE(new_element) &&
8390 !IS_DIGGABLE(new_element) &&
8391 !IS_COLLECTIBLE(new_element))
8394 if (AmoebaNr[newx][newy] &&
8395 (new_element == EL_AMOEBA_FULL ||
8396 new_element == EL_BD_AMOEBA ||
8397 new_element == EL_AMOEBA_GROWING))
8399 AmoebaCnt[AmoebaNr[newx][newy]]--;
8400 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8403 if (IS_MOVING(newx, newy))
8404 RemoveMovingField(newx, newy);
8407 RemoveField(newx, newy);
8408 DrawLevelField(newx, newy);
8411 /* if digged element was about to explode, prevent the explosion */
8412 ExplodeField[newx][newy] = EX_TYPE_NONE;
8414 PlayLevelSoundAction(x, y, action);
8417 Store[newx][newy] = EL_EMPTY;
8419 /* this makes it possible to leave the removed element again */
8420 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8421 Store[newx][newy] = new_element;
8423 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8425 int move_leave_element = element_info[element].move_leave_element;
8427 /* this makes it possible to leave the removed element again */
8428 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8429 new_element : move_leave_element);
8433 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8435 RunnerVisit[x][y] = FrameCounter;
8436 PlayerVisit[x][y] /= 8; /* expire player visit path */
8439 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8441 if (!IS_FREE(newx, newy))
8443 if (IS_PLAYER(x, y))
8444 DrawPlayerField(x, y);
8446 DrawLevelField(x, y);
8452 boolean wanna_flame = !RND(10);
8453 int dx = newx - x, dy = newy - y;
8454 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8455 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8456 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8457 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8458 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8459 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8462 IS_CLASSIC_ENEMY(element1) ||
8463 IS_CLASSIC_ENEMY(element2)) &&
8464 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8465 element1 != EL_FLAMES && element2 != EL_FLAMES)
8467 ResetGfxAnimation(x, y);
8468 GfxAction[x][y] = ACTION_ATTACKING;
8470 if (IS_PLAYER(x, y))
8471 DrawPlayerField(x, y);
8473 DrawLevelField(x, y);
8475 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8477 MovDelay[x][y] = 50;
8481 RemoveField(newx, newy);
8483 Feld[newx][newy] = EL_FLAMES;
8484 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8487 RemoveField(newx1, newy1);
8489 Feld[newx1][newy1] = EL_FLAMES;
8491 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8494 RemoveField(newx2, newy2);
8496 Feld[newx2][newy2] = EL_FLAMES;
8503 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8504 Feld[newx][newy] == EL_DIAMOND)
8506 if (IS_MOVING(newx, newy))
8507 RemoveMovingField(newx, newy);
8510 Feld[newx][newy] = EL_EMPTY;
8511 DrawLevelField(newx, newy);
8514 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8516 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8517 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8519 if (AmoebaNr[newx][newy])
8521 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8522 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8523 Feld[newx][newy] == EL_BD_AMOEBA)
8524 AmoebaCnt[AmoebaNr[newx][newy]]--;
8529 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8531 RemoveMovingField(newx, newy);
8534 if (IS_MOVING(newx, newy))
8536 RemoveMovingField(newx, newy);
8541 Feld[newx][newy] = EL_EMPTY;
8542 DrawLevelField(newx, newy);
8545 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8547 else if ((element == EL_PACMAN || element == EL_MOLE)
8548 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8550 if (AmoebaNr[newx][newy])
8552 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8553 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8554 Feld[newx][newy] == EL_BD_AMOEBA)
8555 AmoebaCnt[AmoebaNr[newx][newy]]--;
8558 if (element == EL_MOLE)
8560 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8561 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8563 ResetGfxAnimation(x, y);
8564 GfxAction[x][y] = ACTION_DIGGING;
8565 DrawLevelField(x, y);
8567 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8569 return; /* wait for shrinking amoeba */
8571 else /* element == EL_PACMAN */
8573 Feld[newx][newy] = EL_EMPTY;
8574 DrawLevelField(newx, newy);
8575 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8578 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8579 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8580 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8582 /* wait for shrinking amoeba to completely disappear */
8585 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8587 /* object was running against a wall */
8592 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8593 if (move_pattern & MV_ANY_DIRECTION &&
8594 move_pattern == MovDir[x][y])
8596 int blocking_element =
8597 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8599 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8602 element = Feld[x][y]; /* element might have changed */
8606 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8607 DrawLevelElementAnimation(x, y, element);
8609 if (DONT_TOUCH(element))
8610 TestIfBadThingTouchesPlayer(x, y);
8615 InitMovingField(x, y, MovDir[x][y]);
8617 PlayLevelSoundAction(x, y, ACTION_MOVING);
8621 ContinueMoving(x, y);
8624 void ContinueMoving(int x, int y)
8626 int element = Feld[x][y];
8627 struct ElementInfo *ei = &element_info[element];
8628 int direction = MovDir[x][y];
8629 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8630 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8631 int newx = x + dx, newy = y + dy;
8632 int stored = Store[x][y];
8633 int stored_new = Store[newx][newy];
8634 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
8635 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8636 boolean last_line = (newy == lev_fieldy - 1);
8638 MovPos[x][y] += getElementMoveStepsize(x, y);
8640 if (pushed_by_player) /* special case: moving object pushed by player */
8641 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8643 if (ABS(MovPos[x][y]) < TILEX)
8646 int ee = Feld[x][y];
8647 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8648 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8650 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8651 x, y, ABS(MovPos[x][y]),
8653 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8656 DrawLevelField(x, y);
8658 return; /* element is still moving */
8661 /* element reached destination field */
8663 Feld[x][y] = EL_EMPTY;
8664 Feld[newx][newy] = element;
8665 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8667 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
8669 element = Feld[newx][newy] = EL_ACID;
8671 else if (element == EL_MOLE)
8673 Feld[x][y] = EL_SAND;
8675 DrawLevelFieldCrumbledSandNeighbours(x, y);
8677 else if (element == EL_QUICKSAND_FILLING)
8679 element = Feld[newx][newy] = get_next_element(element);
8680 Store[newx][newy] = Store[x][y];
8682 else if (element == EL_QUICKSAND_EMPTYING)
8684 Feld[x][y] = get_next_element(element);
8685 element = Feld[newx][newy] = Store[x][y];
8687 else if (element == EL_QUICKSAND_FAST_FILLING)
8689 element = Feld[newx][newy] = get_next_element(element);
8690 Store[newx][newy] = Store[x][y];
8692 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8694 Feld[x][y] = get_next_element(element);
8695 element = Feld[newx][newy] = Store[x][y];
8697 else if (element == EL_MAGIC_WALL_FILLING)
8699 element = Feld[newx][newy] = get_next_element(element);
8700 if (!game.magic_wall_active)
8701 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8702 Store[newx][newy] = Store[x][y];
8704 else if (element == EL_MAGIC_WALL_EMPTYING)
8706 Feld[x][y] = get_next_element(element);
8707 if (!game.magic_wall_active)
8708 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8709 element = Feld[newx][newy] = Store[x][y];
8711 #if USE_NEW_CUSTOM_VALUE
8712 InitField(newx, newy, FALSE);
8715 else if (element == EL_BD_MAGIC_WALL_FILLING)
8717 element = Feld[newx][newy] = get_next_element(element);
8718 if (!game.magic_wall_active)
8719 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8720 Store[newx][newy] = Store[x][y];
8722 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8724 Feld[x][y] = get_next_element(element);
8725 if (!game.magic_wall_active)
8726 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8727 element = Feld[newx][newy] = Store[x][y];
8729 #if USE_NEW_CUSTOM_VALUE
8730 InitField(newx, newy, FALSE);
8733 else if (element == EL_DC_MAGIC_WALL_FILLING)
8735 element = Feld[newx][newy] = get_next_element(element);
8736 if (!game.magic_wall_active)
8737 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8738 Store[newx][newy] = Store[x][y];
8740 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8742 Feld[x][y] = get_next_element(element);
8743 if (!game.magic_wall_active)
8744 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8745 element = Feld[newx][newy] = Store[x][y];
8747 #if USE_NEW_CUSTOM_VALUE
8748 InitField(newx, newy, FALSE);
8751 else if (element == EL_AMOEBA_DROPPING)
8753 Feld[x][y] = get_next_element(element);
8754 element = Feld[newx][newy] = Store[x][y];
8756 else if (element == EL_SOKOBAN_OBJECT)
8759 Feld[x][y] = Back[x][y];
8761 if (Back[newx][newy])
8762 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8764 Back[x][y] = Back[newx][newy] = 0;
8767 Store[x][y] = EL_EMPTY;
8772 MovDelay[newx][newy] = 0;
8774 if (CAN_CHANGE_OR_HAS_ACTION(element))
8776 /* copy element change control values to new field */
8777 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8778 ChangePage[newx][newy] = ChangePage[x][y];
8779 ChangeCount[newx][newy] = ChangeCount[x][y];
8780 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8783 #if USE_NEW_CUSTOM_VALUE
8784 CustomValue[newx][newy] = CustomValue[x][y];
8787 ChangeDelay[x][y] = 0;
8788 ChangePage[x][y] = -1;
8789 ChangeCount[x][y] = 0;
8790 ChangeEvent[x][y] = -1;
8792 #if USE_NEW_CUSTOM_VALUE
8793 CustomValue[x][y] = 0;
8796 /* copy animation control values to new field */
8797 GfxFrame[newx][newy] = GfxFrame[x][y];
8798 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
8799 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
8800 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
8802 Pushed[x][y] = Pushed[newx][newy] = FALSE;
8804 /* some elements can leave other elements behind after moving */
8806 if (ei->move_leave_element != EL_EMPTY &&
8807 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8808 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8810 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
8811 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8812 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8815 int move_leave_element = ei->move_leave_element;
8819 /* this makes it possible to leave the removed element again */
8820 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8821 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8823 /* this makes it possible to leave the removed element again */
8824 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8825 move_leave_element = stored;
8828 /* this makes it possible to leave the removed element again */
8829 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8830 ei->move_leave_element == EL_TRIGGER_ELEMENT)
8831 move_leave_element = stored;
8834 Feld[x][y] = move_leave_element;
8836 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8837 MovDir[x][y] = direction;
8839 InitField(x, y, FALSE);
8841 if (GFX_CRUMBLED(Feld[x][y]))
8842 DrawLevelFieldCrumbledSandNeighbours(x, y);
8844 if (ELEM_IS_PLAYER(move_leave_element))
8845 RelocatePlayer(x, y, move_leave_element);
8848 /* do this after checking for left-behind element */
8849 ResetGfxAnimation(x, y); /* reset animation values for old field */
8851 if (!CAN_MOVE(element) ||
8852 (CAN_FALL(element) && direction == MV_DOWN &&
8853 (element == EL_SPRING ||
8854 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8855 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8856 GfxDir[x][y] = MovDir[newx][newy] = 0;
8858 DrawLevelField(x, y);
8859 DrawLevelField(newx, newy);
8861 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8863 /* prevent pushed element from moving on in pushed direction */
8864 if (pushed_by_player && CAN_MOVE(element) &&
8865 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8866 !(element_info[element].move_pattern & direction))
8867 TurnRound(newx, newy);
8869 /* prevent elements on conveyor belt from moving on in last direction */
8870 if (pushed_by_conveyor && CAN_FALL(element) &&
8871 direction & MV_HORIZONTAL)
8872 MovDir[newx][newy] = 0;
8874 if (!pushed_by_player)
8876 int nextx = newx + dx, nexty = newy + dy;
8877 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8879 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8881 if (CAN_FALL(element) && direction == MV_DOWN)
8882 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8884 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8885 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8887 #if USE_FIX_IMPACT_COLLISION
8888 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8889 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8893 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8895 TestIfBadThingTouchesPlayer(newx, newy);
8896 TestIfBadThingTouchesFriend(newx, newy);
8898 if (!IS_CUSTOM_ELEMENT(element))
8899 TestIfBadThingTouchesOtherBadThing(newx, newy);
8901 else if (element == EL_PENGUIN)
8902 TestIfFriendTouchesBadThing(newx, newy);
8904 /* give the player one last chance (one more frame) to move away */
8905 if (CAN_FALL(element) && direction == MV_DOWN &&
8906 (last_line || (!IS_FREE(x, newy + 1) &&
8907 (!IS_PLAYER(x, newy + 1) ||
8908 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8911 if (pushed_by_player && !game.use_change_when_pushing_bug)
8913 int push_side = MV_DIR_OPPOSITE(direction);
8914 struct PlayerInfo *player = PLAYERINFO(x, y);
8916 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8917 player->index_bit, push_side);
8918 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8919 player->index_bit, push_side);
8922 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
8923 MovDelay[newx][newy] = 1;
8925 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8927 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
8930 if (ChangePage[newx][newy] != -1) /* delayed change */
8932 int page = ChangePage[newx][newy];
8933 struct ElementChangeInfo *change = &ei->change_page[page];
8935 ChangePage[newx][newy] = -1;
8937 if (change->can_change)
8939 if (ChangeElement(newx, newy, element, page))
8941 if (change->post_change_function)
8942 change->post_change_function(newx, newy);
8946 if (change->has_action)
8947 ExecuteCustomElementAction(newx, newy, element, page);
8951 TestIfElementHitsCustomElement(newx, newy, direction);
8952 TestIfPlayerTouchesCustomElement(newx, newy);
8953 TestIfElementTouchesCustomElement(newx, newy);
8955 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8956 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8957 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8958 MV_DIR_OPPOSITE(direction));
8961 int AmoebeNachbarNr(int ax, int ay)
8964 int element = Feld[ax][ay];
8966 static int xy[4][2] =
8974 for (i = 0; i < NUM_DIRECTIONS; i++)
8976 int x = ax + xy[i][0];
8977 int y = ay + xy[i][1];
8979 if (!IN_LEV_FIELD(x, y))
8982 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8983 group_nr = AmoebaNr[x][y];
8989 void AmoebenVereinigen(int ax, int ay)
8991 int i, x, y, xx, yy;
8992 int new_group_nr = AmoebaNr[ax][ay];
8993 static int xy[4][2] =
9001 if (new_group_nr == 0)
9004 for (i = 0; i < NUM_DIRECTIONS; i++)
9009 if (!IN_LEV_FIELD(x, y))
9012 if ((Feld[x][y] == EL_AMOEBA_FULL ||
9013 Feld[x][y] == EL_BD_AMOEBA ||
9014 Feld[x][y] == EL_AMOEBA_DEAD) &&
9015 AmoebaNr[x][y] != new_group_nr)
9017 int old_group_nr = AmoebaNr[x][y];
9019 if (old_group_nr == 0)
9022 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9023 AmoebaCnt[old_group_nr] = 0;
9024 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9025 AmoebaCnt2[old_group_nr] = 0;
9027 SCAN_PLAYFIELD(xx, yy)
9029 if (AmoebaNr[xx][yy] == old_group_nr)
9030 AmoebaNr[xx][yy] = new_group_nr;
9036 void AmoebeUmwandeln(int ax, int ay)
9040 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9042 int group_nr = AmoebaNr[ax][ay];
9047 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9048 printf("AmoebeUmwandeln(): This should never happen!\n");
9053 SCAN_PLAYFIELD(x, y)
9055 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9058 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9062 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9063 SND_AMOEBA_TURNING_TO_GEM :
9064 SND_AMOEBA_TURNING_TO_ROCK));
9069 static int xy[4][2] =
9077 for (i = 0; i < NUM_DIRECTIONS; i++)
9082 if (!IN_LEV_FIELD(x, y))
9085 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9087 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9088 SND_AMOEBA_TURNING_TO_GEM :
9089 SND_AMOEBA_TURNING_TO_ROCK));
9096 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9099 int group_nr = AmoebaNr[ax][ay];
9100 boolean done = FALSE;
9105 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9106 printf("AmoebeUmwandelnBD(): This should never happen!\n");
9111 SCAN_PLAYFIELD(x, y)
9113 if (AmoebaNr[x][y] == group_nr &&
9114 (Feld[x][y] == EL_AMOEBA_DEAD ||
9115 Feld[x][y] == EL_BD_AMOEBA ||
9116 Feld[x][y] == EL_AMOEBA_GROWING))
9119 Feld[x][y] = new_element;
9120 InitField(x, y, FALSE);
9121 DrawLevelField(x, y);
9127 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9128 SND_BD_AMOEBA_TURNING_TO_ROCK :
9129 SND_BD_AMOEBA_TURNING_TO_GEM));
9132 void AmoebeWaechst(int x, int y)
9134 static unsigned long sound_delay = 0;
9135 static unsigned long sound_delay_value = 0;
9137 if (!MovDelay[x][y]) /* start new growing cycle */
9141 if (DelayReached(&sound_delay, sound_delay_value))
9143 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9144 sound_delay_value = 30;
9148 if (MovDelay[x][y]) /* wait some time before growing bigger */
9151 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9153 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9154 6 - MovDelay[x][y]);
9156 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9159 if (!MovDelay[x][y])
9161 Feld[x][y] = Store[x][y];
9163 DrawLevelField(x, y);
9168 void AmoebaDisappearing(int x, int y)
9170 static unsigned long sound_delay = 0;
9171 static unsigned long sound_delay_value = 0;
9173 if (!MovDelay[x][y]) /* start new shrinking cycle */
9177 if (DelayReached(&sound_delay, sound_delay_value))
9178 sound_delay_value = 30;
9181 if (MovDelay[x][y]) /* wait some time before shrinking */
9184 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9186 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9187 6 - MovDelay[x][y]);
9189 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9192 if (!MovDelay[x][y])
9194 Feld[x][y] = EL_EMPTY;
9195 DrawLevelField(x, y);
9197 /* don't let mole enter this field in this cycle;
9198 (give priority to objects falling to this field from above) */
9204 void AmoebeAbleger(int ax, int ay)
9207 int element = Feld[ax][ay];
9208 int graphic = el2img(element);
9209 int newax = ax, neway = ay;
9210 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9211 static int xy[4][2] =
9219 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9221 Feld[ax][ay] = EL_AMOEBA_DEAD;
9222 DrawLevelField(ax, ay);
9226 if (IS_ANIMATED(graphic))
9227 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9229 if (!MovDelay[ax][ay]) /* start making new amoeba field */
9230 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9232 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
9235 if (MovDelay[ax][ay])
9239 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9242 int x = ax + xy[start][0];
9243 int y = ay + xy[start][1];
9245 if (!IN_LEV_FIELD(x, y))
9248 if (IS_FREE(x, y) ||
9249 CAN_GROW_INTO(Feld[x][y]) ||
9250 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9251 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9257 if (newax == ax && neway == ay)
9260 else /* normal or "filled" (BD style) amoeba */
9263 boolean waiting_for_player = FALSE;
9265 for (i = 0; i < NUM_DIRECTIONS; i++)
9267 int j = (start + i) % 4;
9268 int x = ax + xy[j][0];
9269 int y = ay + xy[j][1];
9271 if (!IN_LEV_FIELD(x, y))
9274 if (IS_FREE(x, y) ||
9275 CAN_GROW_INTO(Feld[x][y]) ||
9276 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9277 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9283 else if (IS_PLAYER(x, y))
9284 waiting_for_player = TRUE;
9287 if (newax == ax && neway == ay) /* amoeba cannot grow */
9289 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9291 Feld[ax][ay] = EL_AMOEBA_DEAD;
9292 DrawLevelField(ax, ay);
9293 AmoebaCnt[AmoebaNr[ax][ay]]--;
9295 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
9297 if (element == EL_AMOEBA_FULL)
9298 AmoebeUmwandeln(ax, ay);
9299 else if (element == EL_BD_AMOEBA)
9300 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9305 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9307 /* amoeba gets larger by growing in some direction */
9309 int new_group_nr = AmoebaNr[ax][ay];
9312 if (new_group_nr == 0)
9314 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9315 printf("AmoebeAbleger(): This should never happen!\n");
9320 AmoebaNr[newax][neway] = new_group_nr;
9321 AmoebaCnt[new_group_nr]++;
9322 AmoebaCnt2[new_group_nr]++;
9324 /* if amoeba touches other amoeba(s) after growing, unify them */
9325 AmoebenVereinigen(newax, neway);
9327 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9329 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9335 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9336 (neway == lev_fieldy - 1 && newax != ax))
9338 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
9339 Store[newax][neway] = element;
9341 else if (neway == ay || element == EL_EMC_DRIPPER)
9343 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
9345 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9349 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
9350 Feld[ax][ay] = EL_AMOEBA_DROPPING;
9351 Store[ax][ay] = EL_AMOEBA_DROP;
9352 ContinueMoving(ax, ay);
9356 DrawLevelField(newax, neway);
9359 void Life(int ax, int ay)
9363 int element = Feld[ax][ay];
9364 int graphic = el2img(element);
9365 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9367 boolean changed = FALSE;
9369 if (IS_ANIMATED(graphic))
9370 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9375 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
9376 MovDelay[ax][ay] = life_time;
9378 if (MovDelay[ax][ay]) /* wait some time before next cycle */
9381 if (MovDelay[ax][ay])
9385 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9387 int xx = ax+x1, yy = ay+y1;
9390 if (!IN_LEV_FIELD(xx, yy))
9393 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9395 int x = xx+x2, y = yy+y2;
9397 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9400 if (((Feld[x][y] == element ||
9401 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9403 (IS_FREE(x, y) && Stop[x][y]))
9407 if (xx == ax && yy == ay) /* field in the middle */
9409 if (nachbarn < life_parameter[0] ||
9410 nachbarn > life_parameter[1])
9412 Feld[xx][yy] = EL_EMPTY;
9414 DrawLevelField(xx, yy);
9415 Stop[xx][yy] = TRUE;
9419 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9420 { /* free border field */
9421 if (nachbarn >= life_parameter[2] &&
9422 nachbarn <= life_parameter[3])
9424 Feld[xx][yy] = element;
9425 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9427 DrawLevelField(xx, yy);
9428 Stop[xx][yy] = TRUE;
9435 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9436 SND_GAME_OF_LIFE_GROWING);
9439 static void InitRobotWheel(int x, int y)
9441 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9444 static void RunRobotWheel(int x, int y)
9446 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9449 static void StopRobotWheel(int x, int y)
9451 if (ZX == x && ZY == y)
9455 game.robot_wheel_active = FALSE;
9459 static void InitTimegateWheel(int x, int y)
9461 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9464 static void RunTimegateWheel(int x, int y)
9466 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9469 static void InitMagicBallDelay(int x, int y)
9472 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9474 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9478 static void ActivateMagicBall(int bx, int by)
9482 if (level.ball_random)
9484 int pos_border = RND(8); /* select one of the eight border elements */
9485 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9486 int xx = pos_content % 3;
9487 int yy = pos_content / 3;
9492 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9493 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9497 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9499 int xx = x - bx + 1;
9500 int yy = y - by + 1;
9502 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9503 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9507 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9510 void CheckExit(int x, int y)
9512 if (local_player->gems_still_needed > 0 ||
9513 local_player->sokobanfields_still_needed > 0 ||
9514 local_player->lights_still_needed > 0)
9516 int element = Feld[x][y];
9517 int graphic = el2img(element);
9519 if (IS_ANIMATED(graphic))
9520 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9525 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9528 Feld[x][y] = EL_EXIT_OPENING;
9530 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9533 void CheckExitEM(int x, int y)
9535 if (local_player->gems_still_needed > 0 ||
9536 local_player->sokobanfields_still_needed > 0 ||
9537 local_player->lights_still_needed > 0)
9539 int element = Feld[x][y];
9540 int graphic = el2img(element);
9542 if (IS_ANIMATED(graphic))
9543 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9548 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9551 Feld[x][y] = EL_EM_EXIT_OPENING;
9553 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9556 void CheckExitSteel(int x, int y)
9558 if (local_player->gems_still_needed > 0 ||
9559 local_player->sokobanfields_still_needed > 0 ||
9560 local_player->lights_still_needed > 0)
9562 int element = Feld[x][y];
9563 int graphic = el2img(element);
9565 if (IS_ANIMATED(graphic))
9566 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9571 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9574 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9576 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9579 void CheckExitSteelEM(int x, int y)
9581 if (local_player->gems_still_needed > 0 ||
9582 local_player->sokobanfields_still_needed > 0 ||
9583 local_player->lights_still_needed > 0)
9585 int element = Feld[x][y];
9586 int graphic = el2img(element);
9588 if (IS_ANIMATED(graphic))
9589 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9594 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9597 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9599 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9602 void CheckExitSP(int x, int y)
9604 if (local_player->gems_still_needed > 0)
9606 int element = Feld[x][y];
9607 int graphic = el2img(element);
9609 if (IS_ANIMATED(graphic))
9610 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9615 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9618 Feld[x][y] = EL_SP_EXIT_OPENING;
9620 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9623 static void CloseAllOpenTimegates()
9627 SCAN_PLAYFIELD(x, y)
9629 int element = Feld[x][y];
9631 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9633 Feld[x][y] = EL_TIMEGATE_CLOSING;
9635 PlayLevelSoundAction(x, y, ACTION_CLOSING);
9640 void DrawTwinkleOnField(int x, int y)
9642 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9645 if (Feld[x][y] == EL_BD_DIAMOND)
9648 if (MovDelay[x][y] == 0) /* next animation frame */
9649 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9651 if (MovDelay[x][y] != 0) /* wait some time before next frame */
9655 DrawLevelElementAnimation(x, y, Feld[x][y]);
9657 if (MovDelay[x][y] != 0)
9659 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9660 10 - MovDelay[x][y]);
9662 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9667 void MauerWaechst(int x, int y)
9671 if (!MovDelay[x][y]) /* next animation frame */
9672 MovDelay[x][y] = 3 * delay;
9674 if (MovDelay[x][y]) /* wait some time before next frame */
9678 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9680 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9681 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9683 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9686 if (!MovDelay[x][y])
9688 if (MovDir[x][y] == MV_LEFT)
9690 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9691 DrawLevelField(x - 1, y);
9693 else if (MovDir[x][y] == MV_RIGHT)
9695 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9696 DrawLevelField(x + 1, y);
9698 else if (MovDir[x][y] == MV_UP)
9700 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9701 DrawLevelField(x, y - 1);
9705 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9706 DrawLevelField(x, y + 1);
9709 Feld[x][y] = Store[x][y];
9711 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9712 DrawLevelField(x, y);
9717 void MauerAbleger(int ax, int ay)
9719 int element = Feld[ax][ay];
9720 int graphic = el2img(element);
9721 boolean oben_frei = FALSE, unten_frei = FALSE;
9722 boolean links_frei = FALSE, rechts_frei = FALSE;
9723 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9724 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9725 boolean new_wall = FALSE;
9727 if (IS_ANIMATED(graphic))
9728 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9730 if (!MovDelay[ax][ay]) /* start building new wall */
9731 MovDelay[ax][ay] = 6;
9733 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9736 if (MovDelay[ax][ay])
9740 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9742 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9744 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9746 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9749 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9750 element == EL_EXPANDABLE_WALL_ANY)
9754 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9755 Store[ax][ay-1] = element;
9756 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9757 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9758 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9759 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9764 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9765 Store[ax][ay+1] = element;
9766 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9767 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9768 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9769 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9774 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9775 element == EL_EXPANDABLE_WALL_ANY ||
9776 element == EL_EXPANDABLE_WALL ||
9777 element == EL_BD_EXPANDABLE_WALL)
9781 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9782 Store[ax-1][ay] = element;
9783 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9784 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9785 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9786 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9792 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9793 Store[ax+1][ay] = element;
9794 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9795 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9796 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9797 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9802 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9803 DrawLevelField(ax, ay);
9805 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9807 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9808 unten_massiv = TRUE;
9809 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9810 links_massiv = TRUE;
9811 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9812 rechts_massiv = TRUE;
9814 if (((oben_massiv && unten_massiv) ||
9815 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9816 element == EL_EXPANDABLE_WALL) &&
9817 ((links_massiv && rechts_massiv) ||
9818 element == EL_EXPANDABLE_WALL_VERTICAL))
9819 Feld[ax][ay] = EL_WALL;
9822 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9825 void MauerAblegerStahl(int ax, int ay)
9827 int element = Feld[ax][ay];
9828 int graphic = el2img(element);
9829 boolean oben_frei = FALSE, unten_frei = FALSE;
9830 boolean links_frei = FALSE, rechts_frei = FALSE;
9831 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9832 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9833 boolean new_wall = FALSE;
9835 if (IS_ANIMATED(graphic))
9836 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9838 if (!MovDelay[ax][ay]) /* start building new wall */
9839 MovDelay[ax][ay] = 6;
9841 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9844 if (MovDelay[ax][ay])
9848 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9850 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9852 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9854 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9857 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9858 element == EL_EXPANDABLE_STEELWALL_ANY)
9862 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9863 Store[ax][ay-1] = element;
9864 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9865 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9866 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9867 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9872 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9873 Store[ax][ay+1] = element;
9874 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9875 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9876 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9877 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9882 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9883 element == EL_EXPANDABLE_STEELWALL_ANY)
9887 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9888 Store[ax-1][ay] = element;
9889 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9890 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9891 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9892 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9898 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9899 Store[ax+1][ay] = element;
9900 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9901 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9902 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9903 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9908 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9910 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9911 unten_massiv = TRUE;
9912 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9913 links_massiv = TRUE;
9914 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9915 rechts_massiv = TRUE;
9917 if (((oben_massiv && unten_massiv) ||
9918 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9919 ((links_massiv && rechts_massiv) ||
9920 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9921 Feld[ax][ay] = EL_STEELWALL;
9924 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9927 void CheckForDragon(int x, int y)
9930 boolean dragon_found = FALSE;
9931 static int xy[4][2] =
9939 for (i = 0; i < NUM_DIRECTIONS; i++)
9941 for (j = 0; j < 4; j++)
9943 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9945 if (IN_LEV_FIELD(xx, yy) &&
9946 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9948 if (Feld[xx][yy] == EL_DRAGON)
9949 dragon_found = TRUE;
9958 for (i = 0; i < NUM_DIRECTIONS; i++)
9960 for (j = 0; j < 3; j++)
9962 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9964 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9966 Feld[xx][yy] = EL_EMPTY;
9967 DrawLevelField(xx, yy);
9976 static void InitBuggyBase(int x, int y)
9978 int element = Feld[x][y];
9979 int activating_delay = FRAMES_PER_SECOND / 4;
9982 (element == EL_SP_BUGGY_BASE ?
9983 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9984 element == EL_SP_BUGGY_BASE_ACTIVATING ?
9986 element == EL_SP_BUGGY_BASE_ACTIVE ?
9987 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9990 static void WarnBuggyBase(int x, int y)
9993 static int xy[4][2] =
10001 for (i = 0; i < NUM_DIRECTIONS; i++)
10003 int xx = x + xy[i][0];
10004 int yy = y + xy[i][1];
10006 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10008 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10015 static void InitTrap(int x, int y)
10017 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10020 static void ActivateTrap(int x, int y)
10022 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10025 static void ChangeActiveTrap(int x, int y)
10027 int graphic = IMG_TRAP_ACTIVE;
10029 /* if new animation frame was drawn, correct crumbled sand border */
10030 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10031 DrawLevelFieldCrumbledSand(x, y);
10034 static int getSpecialActionElement(int element, int number, int base_element)
10036 return (element != EL_EMPTY ? element :
10037 number != -1 ? base_element + number - 1 :
10041 static int getModifiedActionNumber(int value_old, int operator, int operand,
10042 int value_min, int value_max)
10044 int value_new = (operator == CA_MODE_SET ? operand :
10045 operator == CA_MODE_ADD ? value_old + operand :
10046 operator == CA_MODE_SUBTRACT ? value_old - operand :
10047 operator == CA_MODE_MULTIPLY ? value_old * operand :
10048 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
10049 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
10052 return (value_new < value_min ? value_min :
10053 value_new > value_max ? value_max :
10057 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10059 struct ElementInfo *ei = &element_info[element];
10060 struct ElementChangeInfo *change = &ei->change_page[page];
10061 int target_element = change->target_element;
10062 int action_type = change->action_type;
10063 int action_mode = change->action_mode;
10064 int action_arg = change->action_arg;
10067 if (!change->has_action)
10070 /* ---------- determine action paramater values -------------------------- */
10072 int level_time_value =
10073 (level.time > 0 ? TimeLeft :
10076 int action_arg_element =
10077 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
10078 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10079 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
10082 int action_arg_direction =
10083 (action_arg >= CA_ARG_DIRECTION_LEFT &&
10084 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10085 action_arg == CA_ARG_DIRECTION_TRIGGER ?
10086 change->actual_trigger_side :
10087 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10088 MV_DIR_OPPOSITE(change->actual_trigger_side) :
10091 int action_arg_number_min =
10092 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10095 int action_arg_number_max =
10096 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10097 action_type == CA_SET_LEVEL_GEMS ? 999 :
10098 action_type == CA_SET_LEVEL_TIME ? 9999 :
10099 action_type == CA_SET_LEVEL_SCORE ? 99999 :
10100 action_type == CA_SET_CE_VALUE ? 9999 :
10101 action_type == CA_SET_CE_SCORE ? 9999 :
10104 int action_arg_number_reset =
10105 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10106 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10107 action_type == CA_SET_LEVEL_TIME ? level.time :
10108 action_type == CA_SET_LEVEL_SCORE ? 0 :
10109 #if USE_NEW_CUSTOM_VALUE
10110 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10112 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10114 action_type == CA_SET_CE_SCORE ? 0 :
10117 int action_arg_number =
10118 (action_arg <= CA_ARG_MAX ? action_arg :
10119 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10120 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10121 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10122 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10123 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10124 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10125 #if USE_NEW_CUSTOM_VALUE
10126 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10128 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10130 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10131 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10132 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10133 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10134 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10135 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10136 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10137 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10138 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10139 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
10140 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10143 int action_arg_number_old =
10144 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10145 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10146 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10147 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10148 action_type == CA_SET_CE_SCORE ? ei->collect_score :
10151 int action_arg_number_new =
10152 getModifiedActionNumber(action_arg_number_old,
10153 action_mode, action_arg_number,
10154 action_arg_number_min, action_arg_number_max);
10157 int trigger_player_bits = change->actual_trigger_player_bits;
10159 int trigger_player_bits =
10160 (change->actual_trigger_player >= EL_PLAYER_1 &&
10161 change->actual_trigger_player <= EL_PLAYER_4 ?
10162 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10166 int action_arg_player_bits =
10167 (action_arg >= CA_ARG_PLAYER_1 &&
10168 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10169 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10172 /* ---------- execute action -------------------------------------------- */
10174 switch (action_type)
10181 /* ---------- level actions ------------------------------------------- */
10183 case CA_RESTART_LEVEL:
10185 game.restart_level = TRUE;
10190 case CA_SHOW_ENVELOPE:
10192 int element = getSpecialActionElement(action_arg_element,
10193 action_arg_number, EL_ENVELOPE_1);
10195 if (IS_ENVELOPE(element))
10196 local_player->show_envelope = element;
10201 case CA_SET_LEVEL_TIME:
10203 if (level.time > 0) /* only modify limited time value */
10205 TimeLeft = action_arg_number_new;
10208 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10210 DisplayGameControlValues();
10212 DrawGameValue_Time(TimeLeft);
10215 if (!TimeLeft && setup.time_limit)
10216 for (i = 0; i < MAX_PLAYERS; i++)
10217 KillPlayer(&stored_player[i]);
10223 case CA_SET_LEVEL_SCORE:
10225 local_player->score = action_arg_number_new;
10228 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10230 DisplayGameControlValues();
10232 DrawGameValue_Score(local_player->score);
10238 case CA_SET_LEVEL_GEMS:
10240 local_player->gems_still_needed = action_arg_number_new;
10243 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10245 DisplayGameControlValues();
10247 DrawGameValue_Emeralds(local_player->gems_still_needed);
10253 #if !USE_PLAYER_GRAVITY
10254 case CA_SET_LEVEL_GRAVITY:
10256 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10257 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10258 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10264 case CA_SET_LEVEL_WIND:
10266 game.wind_direction = action_arg_direction;
10271 /* ---------- player actions ------------------------------------------ */
10273 case CA_MOVE_PLAYER:
10275 /* automatically move to the next field in specified direction */
10276 for (i = 0; i < MAX_PLAYERS; i++)
10277 if (trigger_player_bits & (1 << i))
10278 stored_player[i].programmed_action = action_arg_direction;
10283 case CA_EXIT_PLAYER:
10285 for (i = 0; i < MAX_PLAYERS; i++)
10286 if (action_arg_player_bits & (1 << i))
10287 PlayerWins(&stored_player[i]);
10292 case CA_KILL_PLAYER:
10294 for (i = 0; i < MAX_PLAYERS; i++)
10295 if (action_arg_player_bits & (1 << i))
10296 KillPlayer(&stored_player[i]);
10301 case CA_SET_PLAYER_KEYS:
10303 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10304 int element = getSpecialActionElement(action_arg_element,
10305 action_arg_number, EL_KEY_1);
10307 if (IS_KEY(element))
10309 for (i = 0; i < MAX_PLAYERS; i++)
10311 if (trigger_player_bits & (1 << i))
10313 stored_player[i].key[KEY_NR(element)] = key_state;
10315 DrawGameDoorValues();
10323 case CA_SET_PLAYER_SPEED:
10325 for (i = 0; i < MAX_PLAYERS; i++)
10327 if (trigger_player_bits & (1 << i))
10329 int move_stepsize = TILEX / stored_player[i].move_delay_value;
10331 if (action_arg == CA_ARG_SPEED_FASTER &&
10332 stored_player[i].cannot_move)
10334 action_arg_number = STEPSIZE_VERY_SLOW;
10336 else if (action_arg == CA_ARG_SPEED_SLOWER ||
10337 action_arg == CA_ARG_SPEED_FASTER)
10339 action_arg_number = 2;
10340 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10343 else if (action_arg == CA_ARG_NUMBER_RESET)
10345 action_arg_number = level.initial_player_stepsize[i];
10349 getModifiedActionNumber(move_stepsize,
10352 action_arg_number_min,
10353 action_arg_number_max);
10355 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10362 case CA_SET_PLAYER_SHIELD:
10364 for (i = 0; i < MAX_PLAYERS; i++)
10366 if (trigger_player_bits & (1 << i))
10368 if (action_arg == CA_ARG_SHIELD_OFF)
10370 stored_player[i].shield_normal_time_left = 0;
10371 stored_player[i].shield_deadly_time_left = 0;
10373 else if (action_arg == CA_ARG_SHIELD_NORMAL)
10375 stored_player[i].shield_normal_time_left = 999999;
10377 else if (action_arg == CA_ARG_SHIELD_DEADLY)
10379 stored_player[i].shield_normal_time_left = 999999;
10380 stored_player[i].shield_deadly_time_left = 999999;
10388 #if USE_PLAYER_GRAVITY
10389 case CA_SET_PLAYER_GRAVITY:
10391 for (i = 0; i < MAX_PLAYERS; i++)
10393 if (trigger_player_bits & (1 << i))
10395 stored_player[i].gravity =
10396 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10397 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10398 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10399 stored_player[i].gravity);
10407 case CA_SET_PLAYER_ARTWORK:
10409 for (i = 0; i < MAX_PLAYERS; i++)
10411 if (trigger_player_bits & (1 << i))
10413 int artwork_element = action_arg_element;
10415 if (action_arg == CA_ARG_ELEMENT_RESET)
10417 (level.use_artwork_element[i] ? level.artwork_element[i] :
10418 stored_player[i].element_nr);
10420 #if USE_GFX_RESET_PLAYER_ARTWORK
10421 if (stored_player[i].artwork_element != artwork_element)
10422 stored_player[i].Frame = 0;
10425 stored_player[i].artwork_element = artwork_element;
10427 SetPlayerWaiting(&stored_player[i], FALSE);
10429 /* set number of special actions for bored and sleeping animation */
10430 stored_player[i].num_special_action_bored =
10431 get_num_special_action(artwork_element,
10432 ACTION_BORING_1, ACTION_BORING_LAST);
10433 stored_player[i].num_special_action_sleeping =
10434 get_num_special_action(artwork_element,
10435 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10442 /* ---------- CE actions ---------------------------------------------- */
10444 case CA_SET_CE_VALUE:
10446 #if USE_NEW_CUSTOM_VALUE
10447 int last_ce_value = CustomValue[x][y];
10449 CustomValue[x][y] = action_arg_number_new;
10451 if (CustomValue[x][y] != last_ce_value)
10453 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10454 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10456 if (CustomValue[x][y] == 0)
10458 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10459 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10467 case CA_SET_CE_SCORE:
10469 #if USE_NEW_CUSTOM_VALUE
10470 int last_ce_score = ei->collect_score;
10472 ei->collect_score = action_arg_number_new;
10474 if (ei->collect_score != last_ce_score)
10476 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10477 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10479 if (ei->collect_score == 0)
10483 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10484 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10487 This is a very special case that seems to be a mixture between
10488 CheckElementChange() and CheckTriggeredElementChange(): while
10489 the first one only affects single elements that are triggered
10490 directly, the second one affects multiple elements in the playfield
10491 that are triggered indirectly by another element. This is a third
10492 case: Changing the CE score always affects multiple identical CEs,
10493 so every affected CE must be checked, not only the single CE for
10494 which the CE score was changed in the first place (as every instance
10495 of that CE shares the same CE score, and therefore also can change)!
10497 SCAN_PLAYFIELD(xx, yy)
10499 if (Feld[xx][yy] == element)
10500 CheckElementChange(xx, yy, element, EL_UNDEFINED,
10501 CE_SCORE_GETS_ZERO);
10510 /* ---------- engine actions ------------------------------------------ */
10512 case CA_SET_ENGINE_SCAN_MODE:
10514 InitPlayfieldScanMode(action_arg);
10524 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10526 int old_element = Feld[x][y];
10527 int new_element = GetElementFromGroupElement(element);
10528 int previous_move_direction = MovDir[x][y];
10529 #if USE_NEW_CUSTOM_VALUE
10530 int last_ce_value = CustomValue[x][y];
10532 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10533 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10534 boolean add_player_onto_element = (new_element_is_player &&
10535 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
10536 /* this breaks SnakeBite when a snake is
10537 halfway through a door that closes */
10538 /* NOW FIXED AT LEVEL INIT IN files.c */
10539 new_element != EL_SOKOBAN_FIELD_PLAYER &&
10541 IS_WALKABLE(old_element));
10544 /* check if element under the player changes from accessible to unaccessible
10545 (needed for special case of dropping element which then changes) */
10546 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10547 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10555 if (!add_player_onto_element)
10557 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10558 RemoveMovingField(x, y);
10562 Feld[x][y] = new_element;
10564 #if !USE_GFX_RESET_GFX_ANIMATION
10565 ResetGfxAnimation(x, y);
10566 ResetRandomAnimationValue(x, y);
10569 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10570 MovDir[x][y] = previous_move_direction;
10572 #if USE_NEW_CUSTOM_VALUE
10573 if (element_info[new_element].use_last_ce_value)
10574 CustomValue[x][y] = last_ce_value;
10577 InitField_WithBug1(x, y, FALSE);
10579 new_element = Feld[x][y]; /* element may have changed */
10581 #if USE_GFX_RESET_GFX_ANIMATION
10582 ResetGfxAnimation(x, y);
10583 ResetRandomAnimationValue(x, y);
10586 DrawLevelField(x, y);
10588 if (GFX_CRUMBLED(new_element))
10589 DrawLevelFieldCrumbledSandNeighbours(x, y);
10593 /* check if element under the player changes from accessible to unaccessible
10594 (needed for special case of dropping element which then changes) */
10595 /* (must be checked after creating new element for walkable group elements) */
10596 #if USE_FIX_KILLED_BY_NON_WALKABLE
10597 if (IS_PLAYER(x, y) && !player_explosion_protected &&
10598 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10605 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10606 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10615 /* "ChangeCount" not set yet to allow "entered by player" change one time */
10616 if (new_element_is_player)
10617 RelocatePlayer(x, y, new_element);
10620 ChangeCount[x][y]++; /* count number of changes in the same frame */
10622 TestIfBadThingTouchesPlayer(x, y);
10623 TestIfPlayerTouchesCustomElement(x, y);
10624 TestIfElementTouchesCustomElement(x, y);
10627 static void CreateField(int x, int y, int element)
10629 CreateFieldExt(x, y, element, FALSE);
10632 static void CreateElementFromChange(int x, int y, int element)
10634 element = GET_VALID_RUNTIME_ELEMENT(element);
10636 #if USE_STOP_CHANGED_ELEMENTS
10637 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10639 int old_element = Feld[x][y];
10641 /* prevent changed element from moving in same engine frame
10642 unless both old and new element can either fall or move */
10643 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10644 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10649 CreateFieldExt(x, y, element, TRUE);
10652 static boolean ChangeElement(int x, int y, int element, int page)
10654 struct ElementInfo *ei = &element_info[element];
10655 struct ElementChangeInfo *change = &ei->change_page[page];
10656 int ce_value = CustomValue[x][y];
10657 int ce_score = ei->collect_score;
10658 int target_element;
10659 int old_element = Feld[x][y];
10661 /* always use default change event to prevent running into a loop */
10662 if (ChangeEvent[x][y] == -1)
10663 ChangeEvent[x][y] = CE_DELAY;
10665 if (ChangeEvent[x][y] == CE_DELAY)
10667 /* reset actual trigger element, trigger player and action element */
10668 change->actual_trigger_element = EL_EMPTY;
10669 change->actual_trigger_player = EL_PLAYER_1;
10670 change->actual_trigger_player_bits = CH_PLAYER_1;
10671 change->actual_trigger_side = CH_SIDE_NONE;
10672 change->actual_trigger_ce_value = 0;
10673 change->actual_trigger_ce_score = 0;
10676 /* do not change elements more than a specified maximum number of changes */
10677 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10680 ChangeCount[x][y]++; /* count number of changes in the same frame */
10682 if (change->explode)
10689 if (change->use_target_content)
10691 boolean complete_replace = TRUE;
10692 boolean can_replace[3][3];
10695 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10698 boolean is_walkable;
10699 boolean is_diggable;
10700 boolean is_collectible;
10701 boolean is_removable;
10702 boolean is_destructible;
10703 int ex = x + xx - 1;
10704 int ey = y + yy - 1;
10705 int content_element = change->target_content.e[xx][yy];
10708 can_replace[xx][yy] = TRUE;
10710 if (ex == x && ey == y) /* do not check changing element itself */
10713 if (content_element == EL_EMPTY_SPACE)
10715 can_replace[xx][yy] = FALSE; /* do not replace border with space */
10720 if (!IN_LEV_FIELD(ex, ey))
10722 can_replace[xx][yy] = FALSE;
10723 complete_replace = FALSE;
10730 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10731 e = MovingOrBlocked2Element(ex, ey);
10733 is_empty = (IS_FREE(ex, ey) ||
10734 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10736 is_walkable = (is_empty || IS_WALKABLE(e));
10737 is_diggable = (is_empty || IS_DIGGABLE(e));
10738 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10739 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10740 is_removable = (is_diggable || is_collectible);
10742 can_replace[xx][yy] =
10743 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10744 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10745 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10746 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10747 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10748 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10749 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10751 if (!can_replace[xx][yy])
10752 complete_replace = FALSE;
10755 if (!change->only_if_complete || complete_replace)
10757 boolean something_has_changed = FALSE;
10759 if (change->only_if_complete && change->use_random_replace &&
10760 RND(100) < change->random_percentage)
10763 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10765 int ex = x + xx - 1;
10766 int ey = y + yy - 1;
10767 int content_element;
10769 if (can_replace[xx][yy] && (!change->use_random_replace ||
10770 RND(100) < change->random_percentage))
10772 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10773 RemoveMovingField(ex, ey);
10775 ChangeEvent[ex][ey] = ChangeEvent[x][y];
10777 content_element = change->target_content.e[xx][yy];
10778 target_element = GET_TARGET_ELEMENT(element, content_element, change,
10779 ce_value, ce_score);
10781 CreateElementFromChange(ex, ey, target_element);
10783 something_has_changed = TRUE;
10785 /* for symmetry reasons, freeze newly created border elements */
10786 if (ex != x || ey != y)
10787 Stop[ex][ey] = TRUE; /* no more moving in this frame */
10791 if (something_has_changed)
10793 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10794 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10800 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10801 ce_value, ce_score);
10803 if (element == EL_DIAGONAL_GROWING ||
10804 element == EL_DIAGONAL_SHRINKING)
10806 target_element = Store[x][y];
10808 Store[x][y] = EL_EMPTY;
10811 CreateElementFromChange(x, y, target_element);
10813 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10814 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10817 /* this uses direct change before indirect change */
10818 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10823 #if USE_NEW_DELAYED_ACTION
10825 static void HandleElementChange(int x, int y, int page)
10827 int element = MovingOrBlocked2Element(x, y);
10828 struct ElementInfo *ei = &element_info[element];
10829 struct ElementChangeInfo *change = &ei->change_page[page];
10832 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10833 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10836 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10837 x, y, element, element_info[element].token_name);
10838 printf("HandleElementChange(): This should never happen!\n");
10843 /* this can happen with classic bombs on walkable, changing elements */
10844 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10847 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
10848 ChangeDelay[x][y] = 0;
10854 if (ChangeDelay[x][y] == 0) /* initialize element change */
10856 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10858 if (change->can_change)
10861 /* !!! not clear why graphic animation should be reset at all here !!! */
10862 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10863 #if USE_GFX_RESET_WHEN_NOT_MOVING
10864 /* when a custom element is about to change (for example by change delay),
10865 do not reset graphic animation when the custom element is moving */
10866 if (!IS_MOVING(x, y))
10869 ResetGfxAnimation(x, y);
10870 ResetRandomAnimationValue(x, y);
10874 if (change->pre_change_function)
10875 change->pre_change_function(x, y);
10879 ChangeDelay[x][y]--;
10881 if (ChangeDelay[x][y] != 0) /* continue element change */
10883 if (change->can_change)
10885 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10887 if (IS_ANIMATED(graphic))
10888 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10890 if (change->change_function)
10891 change->change_function(x, y);
10894 else /* finish element change */
10896 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10898 page = ChangePage[x][y];
10899 ChangePage[x][y] = -1;
10901 change = &ei->change_page[page];
10904 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10906 ChangeDelay[x][y] = 1; /* try change after next move step */
10907 ChangePage[x][y] = page; /* remember page to use for change */
10912 if (change->can_change)
10914 if (ChangeElement(x, y, element, page))
10916 if (change->post_change_function)
10917 change->post_change_function(x, y);
10921 if (change->has_action)
10922 ExecuteCustomElementAction(x, y, element, page);
10928 static void HandleElementChange(int x, int y, int page)
10930 int element = MovingOrBlocked2Element(x, y);
10931 struct ElementInfo *ei = &element_info[element];
10932 struct ElementChangeInfo *change = &ei->change_page[page];
10935 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
10938 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10939 x, y, element, element_info[element].token_name);
10940 printf("HandleElementChange(): This should never happen!\n");
10945 /* this can happen with classic bombs on walkable, changing elements */
10946 if (!CAN_CHANGE(element))
10949 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
10950 ChangeDelay[x][y] = 0;
10956 if (ChangeDelay[x][y] == 0) /* initialize element change */
10958 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10960 ResetGfxAnimation(x, y);
10961 ResetRandomAnimationValue(x, y);
10963 if (change->pre_change_function)
10964 change->pre_change_function(x, y);
10967 ChangeDelay[x][y]--;
10969 if (ChangeDelay[x][y] != 0) /* continue element change */
10971 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10973 if (IS_ANIMATED(graphic))
10974 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10976 if (change->change_function)
10977 change->change_function(x, y);
10979 else /* finish element change */
10981 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10983 page = ChangePage[x][y];
10984 ChangePage[x][y] = -1;
10986 change = &ei->change_page[page];
10989 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10991 ChangeDelay[x][y] = 1; /* try change after next move step */
10992 ChangePage[x][y] = page; /* remember page to use for change */
10997 if (ChangeElement(x, y, element, page))
10999 if (change->post_change_function)
11000 change->post_change_function(x, y);
11007 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11008 int trigger_element,
11010 int trigger_player,
11014 boolean change_done_any = FALSE;
11015 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11018 if (!(trigger_events[trigger_element][trigger_event]))
11022 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11023 trigger_event, recursion_loop_depth, recursion_loop_detected,
11024 recursion_loop_element, EL_NAME(recursion_loop_element));
11027 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11029 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11031 int element = EL_CUSTOM_START + i;
11032 boolean change_done = FALSE;
11035 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11036 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11039 for (p = 0; p < element_info[element].num_change_pages; p++)
11041 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11043 if (change->can_change_or_has_action &&
11044 change->has_event[trigger_event] &&
11045 change->trigger_side & trigger_side &&
11046 change->trigger_player & trigger_player &&
11047 change->trigger_page & trigger_page_bits &&
11048 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11050 change->actual_trigger_element = trigger_element;
11051 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11052 change->actual_trigger_player_bits = trigger_player;
11053 change->actual_trigger_side = trigger_side;
11054 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11055 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11057 if ((change->can_change && !change_done) || change->has_action)
11061 SCAN_PLAYFIELD(x, y)
11063 if (Feld[x][y] == element)
11065 if (change->can_change && !change_done)
11067 ChangeDelay[x][y] = 1;
11068 ChangeEvent[x][y] = trigger_event;
11070 HandleElementChange(x, y, p);
11072 #if USE_NEW_DELAYED_ACTION
11073 else if (change->has_action)
11075 ExecuteCustomElementAction(x, y, element, p);
11076 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11079 if (change->has_action)
11081 ExecuteCustomElementAction(x, y, element, p);
11082 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11088 if (change->can_change)
11090 change_done = TRUE;
11091 change_done_any = TRUE;
11098 RECURSION_LOOP_DETECTION_END();
11100 return change_done_any;
11103 static boolean CheckElementChangeExt(int x, int y,
11105 int trigger_element,
11107 int trigger_player,
11110 boolean change_done = FALSE;
11113 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11114 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11117 if (Feld[x][y] == EL_BLOCKED)
11119 Blocked2Moving(x, y, &x, &y);
11120 element = Feld[x][y];
11124 /* check if element has already changed */
11125 if (Feld[x][y] != element)
11128 /* check if element has already changed or is about to change after moving */
11129 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11130 Feld[x][y] != element) ||
11132 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11133 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11134 ChangePage[x][y] != -1)))
11139 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11140 trigger_event, recursion_loop_depth, recursion_loop_detected,
11141 recursion_loop_element, EL_NAME(recursion_loop_element));
11144 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11146 for (p = 0; p < element_info[element].num_change_pages; p++)
11148 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11150 /* check trigger element for all events where the element that is checked
11151 for changing interacts with a directly adjacent element -- this is
11152 different to element changes that affect other elements to change on the
11153 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11154 boolean check_trigger_element =
11155 (trigger_event == CE_TOUCHING_X ||
11156 trigger_event == CE_HITTING_X ||
11157 trigger_event == CE_HIT_BY_X ||
11159 /* this one was forgotten until 3.2.3 */
11160 trigger_event == CE_DIGGING_X);
11163 if (change->can_change_or_has_action &&
11164 change->has_event[trigger_event] &&
11165 change->trigger_side & trigger_side &&
11166 change->trigger_player & trigger_player &&
11167 (!check_trigger_element ||
11168 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11170 change->actual_trigger_element = trigger_element;
11171 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11172 change->actual_trigger_player_bits = trigger_player;
11173 change->actual_trigger_side = trigger_side;
11174 change->actual_trigger_ce_value = CustomValue[x][y];
11175 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11177 /* special case: trigger element not at (x,y) position for some events */
11178 if (check_trigger_element)
11190 { 0, 0 }, { 0, 0 }, { 0, 0 },
11194 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11195 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11197 change->actual_trigger_ce_value = CustomValue[xx][yy];
11198 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11201 if (change->can_change && !change_done)
11203 ChangeDelay[x][y] = 1;
11204 ChangeEvent[x][y] = trigger_event;
11206 HandleElementChange(x, y, p);
11208 change_done = TRUE;
11210 #if USE_NEW_DELAYED_ACTION
11211 else if (change->has_action)
11213 ExecuteCustomElementAction(x, y, element, p);
11214 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11217 if (change->has_action)
11219 ExecuteCustomElementAction(x, y, element, p);
11220 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11226 RECURSION_LOOP_DETECTION_END();
11228 return change_done;
11231 static void PlayPlayerSound(struct PlayerInfo *player)
11233 int jx = player->jx, jy = player->jy;
11234 int sound_element = player->artwork_element;
11235 int last_action = player->last_action_waiting;
11236 int action = player->action_waiting;
11238 if (player->is_waiting)
11240 if (action != last_action)
11241 PlayLevelSoundElementAction(jx, jy, sound_element, action);
11243 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11247 if (action != last_action)
11248 StopSound(element_info[sound_element].sound[last_action]);
11250 if (last_action == ACTION_SLEEPING)
11251 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11255 static void PlayAllPlayersSound()
11259 for (i = 0; i < MAX_PLAYERS; i++)
11260 if (stored_player[i].active)
11261 PlayPlayerSound(&stored_player[i]);
11264 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11266 boolean last_waiting = player->is_waiting;
11267 int move_dir = player->MovDir;
11269 player->dir_waiting = move_dir;
11270 player->last_action_waiting = player->action_waiting;
11274 if (!last_waiting) /* not waiting -> waiting */
11276 player->is_waiting = TRUE;
11278 player->frame_counter_bored =
11280 game.player_boring_delay_fixed +
11281 GetSimpleRandom(game.player_boring_delay_random);
11282 player->frame_counter_sleeping =
11284 game.player_sleeping_delay_fixed +
11285 GetSimpleRandom(game.player_sleeping_delay_random);
11287 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11290 if (game.player_sleeping_delay_fixed +
11291 game.player_sleeping_delay_random > 0 &&
11292 player->anim_delay_counter == 0 &&
11293 player->post_delay_counter == 0 &&
11294 FrameCounter >= player->frame_counter_sleeping)
11295 player->is_sleeping = TRUE;
11296 else if (game.player_boring_delay_fixed +
11297 game.player_boring_delay_random > 0 &&
11298 FrameCounter >= player->frame_counter_bored)
11299 player->is_bored = TRUE;
11301 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11302 player->is_bored ? ACTION_BORING :
11305 if (player->is_sleeping && player->use_murphy)
11307 /* special case for sleeping Murphy when leaning against non-free tile */
11309 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11310 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11311 !IS_MOVING(player->jx - 1, player->jy)))
11312 move_dir = MV_LEFT;
11313 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11314 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11315 !IS_MOVING(player->jx + 1, player->jy)))
11316 move_dir = MV_RIGHT;
11318 player->is_sleeping = FALSE;
11320 player->dir_waiting = move_dir;
11323 if (player->is_sleeping)
11325 if (player->num_special_action_sleeping > 0)
11327 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11329 int last_special_action = player->special_action_sleeping;
11330 int num_special_action = player->num_special_action_sleeping;
11331 int special_action =
11332 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11333 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11334 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11335 last_special_action + 1 : ACTION_SLEEPING);
11336 int special_graphic =
11337 el_act_dir2img(player->artwork_element, special_action, move_dir);
11339 player->anim_delay_counter =
11340 graphic_info[special_graphic].anim_delay_fixed +
11341 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11342 player->post_delay_counter =
11343 graphic_info[special_graphic].post_delay_fixed +
11344 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11346 player->special_action_sleeping = special_action;
11349 if (player->anim_delay_counter > 0)
11351 player->action_waiting = player->special_action_sleeping;
11352 player->anim_delay_counter--;
11354 else if (player->post_delay_counter > 0)
11356 player->post_delay_counter--;
11360 else if (player->is_bored)
11362 if (player->num_special_action_bored > 0)
11364 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11366 int special_action =
11367 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11368 int special_graphic =
11369 el_act_dir2img(player->artwork_element, special_action, move_dir);
11371 player->anim_delay_counter =
11372 graphic_info[special_graphic].anim_delay_fixed +
11373 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11374 player->post_delay_counter =
11375 graphic_info[special_graphic].post_delay_fixed +
11376 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11378 player->special_action_bored = special_action;
11381 if (player->anim_delay_counter > 0)
11383 player->action_waiting = player->special_action_bored;
11384 player->anim_delay_counter--;
11386 else if (player->post_delay_counter > 0)
11388 player->post_delay_counter--;
11393 else if (last_waiting) /* waiting -> not waiting */
11395 player->is_waiting = FALSE;
11396 player->is_bored = FALSE;
11397 player->is_sleeping = FALSE;
11399 player->frame_counter_bored = -1;
11400 player->frame_counter_sleeping = -1;
11402 player->anim_delay_counter = 0;
11403 player->post_delay_counter = 0;
11405 player->dir_waiting = player->MovDir;
11406 player->action_waiting = ACTION_DEFAULT;
11408 player->special_action_bored = ACTION_DEFAULT;
11409 player->special_action_sleeping = ACTION_DEFAULT;
11413 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11415 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11416 int left = player_action & JOY_LEFT;
11417 int right = player_action & JOY_RIGHT;
11418 int up = player_action & JOY_UP;
11419 int down = player_action & JOY_DOWN;
11420 int button1 = player_action & JOY_BUTTON_1;
11421 int button2 = player_action & JOY_BUTTON_2;
11422 int dx = (left ? -1 : right ? 1 : 0);
11423 int dy = (up ? -1 : down ? 1 : 0);
11425 if (!player->active || tape.pausing)
11431 snapped = SnapField(player, dx, dy);
11435 dropped = DropElement(player);
11437 moved = MovePlayer(player, dx, dy);
11440 if (tape.single_step && tape.recording && !tape.pausing)
11442 if (button1 || (dropped && !moved))
11444 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11445 SnapField(player, 0, 0); /* stop snapping */
11449 SetPlayerWaiting(player, FALSE);
11451 return player_action;
11455 /* no actions for this player (no input at player's configured device) */
11457 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11458 SnapField(player, 0, 0);
11459 CheckGravityMovementWhenNotMoving(player);
11461 if (player->MovPos == 0)
11462 SetPlayerWaiting(player, TRUE);
11464 if (player->MovPos == 0) /* needed for tape.playing */
11465 player->is_moving = FALSE;
11467 player->is_dropping = FALSE;
11468 player->is_dropping_pressed = FALSE;
11469 player->drop_pressed_delay = 0;
11475 static void CheckLevelTime()
11479 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11481 if (level.native_em_level->lev->home == 0) /* all players at home */
11483 PlayerWins(local_player);
11485 AllPlayersGone = TRUE;
11487 level.native_em_level->lev->home = -1;
11490 if (level.native_em_level->ply[0]->alive == 0 &&
11491 level.native_em_level->ply[1]->alive == 0 &&
11492 level.native_em_level->ply[2]->alive == 0 &&
11493 level.native_em_level->ply[3]->alive == 0) /* all dead */
11494 AllPlayersGone = TRUE;
11497 if (TimeFrames >= FRAMES_PER_SECOND)
11502 for (i = 0; i < MAX_PLAYERS; i++)
11504 struct PlayerInfo *player = &stored_player[i];
11506 if (SHIELD_ON(player))
11508 player->shield_normal_time_left--;
11510 if (player->shield_deadly_time_left > 0)
11511 player->shield_deadly_time_left--;
11515 if (!local_player->LevelSolved && !level.use_step_counter)
11523 if (TimeLeft <= 10 && setup.time_limit)
11524 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11527 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11529 DisplayGameControlValues();
11531 DrawGameValue_Time(TimeLeft);
11534 if (!TimeLeft && setup.time_limit)
11536 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11537 level.native_em_level->lev->killed_out_of_time = TRUE;
11539 for (i = 0; i < MAX_PLAYERS; i++)
11540 KillPlayer(&stored_player[i]);
11544 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11546 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11548 DisplayGameControlValues();
11551 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11552 DrawGameValue_Time(TimePlayed);
11555 level.native_em_level->lev->time =
11556 (level.time == 0 ? TimePlayed : TimeLeft);
11559 if (tape.recording || tape.playing)
11560 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11564 UpdateAndDisplayGameControlValues();
11566 UpdateGameDoorValues();
11567 DrawGameDoorValues();
11571 void AdvanceFrameAndPlayerCounters(int player_nr)
11575 /* advance frame counters (global frame counter and time frame counter) */
11579 /* advance player counters (counters for move delay, move animation etc.) */
11580 for (i = 0; i < MAX_PLAYERS; i++)
11582 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11583 int move_delay_value = stored_player[i].move_delay_value;
11584 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11586 if (!advance_player_counters) /* not all players may be affected */
11589 #if USE_NEW_PLAYER_ANIM
11590 if (move_frames == 0) /* less than one move per game frame */
11592 int stepsize = TILEX / move_delay_value;
11593 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11594 int count = (stored_player[i].is_moving ?
11595 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11597 if (count % delay == 0)
11602 stored_player[i].Frame += move_frames;
11604 if (stored_player[i].MovPos != 0)
11605 stored_player[i].StepFrame += move_frames;
11607 if (stored_player[i].move_delay > 0)
11608 stored_player[i].move_delay--;
11610 /* due to bugs in previous versions, counter must count up, not down */
11611 if (stored_player[i].push_delay != -1)
11612 stored_player[i].push_delay++;
11614 if (stored_player[i].drop_delay > 0)
11615 stored_player[i].drop_delay--;
11617 if (stored_player[i].is_dropping_pressed)
11618 stored_player[i].drop_pressed_delay++;
11622 void StartGameActions(boolean init_network_game, boolean record_tape,
11625 unsigned long new_random_seed = InitRND(random_seed);
11628 TapeStartRecording(new_random_seed);
11630 #if defined(NETWORK_AVALIABLE)
11631 if (init_network_game)
11633 SendToServer_StartPlaying();
11644 static unsigned long game_frame_delay = 0;
11645 unsigned long game_frame_delay_value;
11646 byte *recorded_player_action;
11647 byte summarized_player_action = 0;
11648 byte tape_action[MAX_PLAYERS];
11651 /* detect endless loops, caused by custom element programming */
11652 if (recursion_loop_detected && recursion_loop_depth == 0)
11654 char *message = getStringCat3("Internal Error ! Element ",
11655 EL_NAME(recursion_loop_element),
11656 " caused endless loop ! Quit the game ?");
11658 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11659 EL_NAME(recursion_loop_element));
11661 RequestQuitGameExt(FALSE, level_editor_test_game, message);
11663 recursion_loop_detected = FALSE; /* if game should be continued */
11670 if (game.restart_level)
11671 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
11673 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11675 if (level.native_em_level->lev->home == 0) /* all players at home */
11677 PlayerWins(local_player);
11679 AllPlayersGone = TRUE;
11681 level.native_em_level->lev->home = -1;
11684 if (level.native_em_level->ply[0]->alive == 0 &&
11685 level.native_em_level->ply[1]->alive == 0 &&
11686 level.native_em_level->ply[2]->alive == 0 &&
11687 level.native_em_level->ply[3]->alive == 0) /* all dead */
11688 AllPlayersGone = TRUE;
11691 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11694 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11697 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
11700 game_frame_delay_value =
11701 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11703 if (tape.playing && tape.warp_forward && !tape.pausing)
11704 game_frame_delay_value = 0;
11706 /* ---------- main game synchronization point ---------- */
11708 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11710 if (network_playing && !network_player_action_received)
11712 /* try to get network player actions in time */
11714 #if defined(NETWORK_AVALIABLE)
11715 /* last chance to get network player actions without main loop delay */
11716 HandleNetworking();
11719 /* game was quit by network peer */
11720 if (game_status != GAME_MODE_PLAYING)
11723 if (!network_player_action_received)
11724 return; /* failed to get network player actions in time */
11726 /* do not yet reset "network_player_action_received" (for tape.pausing) */
11732 /* at this point we know that we really continue executing the game */
11734 network_player_action_received = FALSE;
11736 /* when playing tape, read previously recorded player input from tape data */
11737 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11740 /* TapePlayAction() may return NULL when toggling to "pause before death" */
11745 if (tape.set_centered_player)
11747 game.centered_player_nr_next = tape.centered_player_nr_next;
11748 game.set_centered_player = TRUE;
11751 for (i = 0; i < MAX_PLAYERS; i++)
11753 summarized_player_action |= stored_player[i].action;
11755 if (!network_playing)
11756 stored_player[i].effective_action = stored_player[i].action;
11759 #if defined(NETWORK_AVALIABLE)
11760 if (network_playing)
11761 SendToServer_MovePlayer(summarized_player_action);
11764 if (!options.network && !setup.team_mode)
11765 local_player->effective_action = summarized_player_action;
11767 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
11769 for (i = 0; i < MAX_PLAYERS; i++)
11770 stored_player[i].effective_action =
11771 (i == game.centered_player_nr ? summarized_player_action : 0);
11774 if (recorded_player_action != NULL)
11775 for (i = 0; i < MAX_PLAYERS; i++)
11776 stored_player[i].effective_action = recorded_player_action[i];
11778 for (i = 0; i < MAX_PLAYERS; i++)
11780 tape_action[i] = stored_player[i].effective_action;
11782 /* (this can only happen in the R'n'D game engine) */
11783 if (tape.recording && tape_action[i] && !tape.player_participates[i])
11784 tape.player_participates[i] = TRUE; /* player just appeared from CE */
11787 /* only record actions from input devices, but not programmed actions */
11788 if (tape.recording)
11789 TapeRecordAction(tape_action);
11791 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11793 GameActions_EM_Main();
11801 void GameActions_EM_Main()
11803 byte effective_action[MAX_PLAYERS];
11804 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11807 for (i = 0; i < MAX_PLAYERS; i++)
11808 effective_action[i] = stored_player[i].effective_action;
11810 GameActions_EM(effective_action, warp_mode);
11814 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11817 void GameActions_RND()
11819 int magic_wall_x = 0, magic_wall_y = 0;
11820 int i, x, y, element, graphic;
11822 InitPlayfieldScanModeVars();
11824 #if USE_ONE_MORE_CHANGE_PER_FRAME
11825 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11827 SCAN_PLAYFIELD(x, y)
11829 ChangeCount[x][y] = 0;
11830 ChangeEvent[x][y] = -1;
11835 if (game.set_centered_player)
11837 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11839 /* switching to "all players" only possible if all players fit to screen */
11840 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11842 game.centered_player_nr_next = game.centered_player_nr;
11843 game.set_centered_player = FALSE;
11846 /* do not switch focus to non-existing (or non-active) player */
11847 if (game.centered_player_nr_next >= 0 &&
11848 !stored_player[game.centered_player_nr_next].active)
11850 game.centered_player_nr_next = game.centered_player_nr;
11851 game.set_centered_player = FALSE;
11855 if (game.set_centered_player &&
11856 ScreenMovPos == 0) /* screen currently aligned at tile position */
11860 if (game.centered_player_nr_next == -1)
11862 setScreenCenteredToAllPlayers(&sx, &sy);
11866 sx = stored_player[game.centered_player_nr_next].jx;
11867 sy = stored_player[game.centered_player_nr_next].jy;
11870 game.centered_player_nr = game.centered_player_nr_next;
11871 game.set_centered_player = FALSE;
11873 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11874 DrawGameDoorValues();
11877 for (i = 0; i < MAX_PLAYERS; i++)
11879 int actual_player_action = stored_player[i].effective_action;
11882 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11883 - rnd_equinox_tetrachloride 048
11884 - rnd_equinox_tetrachloride_ii 096
11885 - rnd_emanuel_schmieg 002
11886 - doctor_sloan_ww 001, 020
11888 if (stored_player[i].MovPos == 0)
11889 CheckGravityMovement(&stored_player[i]);
11892 /* overwrite programmed action with tape action */
11893 if (stored_player[i].programmed_action)
11894 actual_player_action = stored_player[i].programmed_action;
11896 PlayerActions(&stored_player[i], actual_player_action);
11898 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11901 ScrollScreen(NULL, SCROLL_GO_ON);
11903 /* for backwards compatibility, the following code emulates a fixed bug that
11904 occured when pushing elements (causing elements that just made their last
11905 pushing step to already (if possible) make their first falling step in the
11906 same game frame, which is bad); this code is also needed to use the famous
11907 "spring push bug" which is used in older levels and might be wanted to be
11908 used also in newer levels, but in this case the buggy pushing code is only
11909 affecting the "spring" element and no other elements */
11911 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11913 for (i = 0; i < MAX_PLAYERS; i++)
11915 struct PlayerInfo *player = &stored_player[i];
11916 int x = player->jx;
11917 int y = player->jy;
11919 if (player->active && player->is_pushing && player->is_moving &&
11921 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11922 Feld[x][y] == EL_SPRING))
11924 ContinueMoving(x, y);
11926 /* continue moving after pushing (this is actually a bug) */
11927 if (!IS_MOVING(x, y))
11928 Stop[x][y] = FALSE;
11934 debug_print_timestamp(0, "start main loop profiling");
11937 SCAN_PLAYFIELD(x, y)
11939 ChangeCount[x][y] = 0;
11940 ChangeEvent[x][y] = -1;
11942 /* this must be handled before main playfield loop */
11943 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11946 if (MovDelay[x][y] <= 0)
11950 #if USE_NEW_SNAP_DELAY
11951 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11954 if (MovDelay[x][y] <= 0)
11957 DrawLevelField(x, y);
11959 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11965 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11967 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11968 printf("GameActions(): This should never happen!\n");
11970 ChangePage[x][y] = -1;
11974 Stop[x][y] = FALSE;
11975 if (WasJustMoving[x][y] > 0)
11976 WasJustMoving[x][y]--;
11977 if (WasJustFalling[x][y] > 0)
11978 WasJustFalling[x][y]--;
11979 if (CheckCollision[x][y] > 0)
11980 CheckCollision[x][y]--;
11981 if (CheckImpact[x][y] > 0)
11982 CheckImpact[x][y]--;
11986 /* reset finished pushing action (not done in ContinueMoving() to allow
11987 continuous pushing animation for elements with zero push delay) */
11988 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11990 ResetGfxAnimation(x, y);
11991 DrawLevelField(x, y);
11995 if (IS_BLOCKED(x, y))
11999 Blocked2Moving(x, y, &oldx, &oldy);
12000 if (!IS_MOVING(oldx, oldy))
12002 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12003 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12004 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12005 printf("GameActions(): This should never happen!\n");
12012 debug_print_timestamp(0, "- time for pre-main loop:");
12015 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
12016 SCAN_PLAYFIELD(x, y)
12018 element = Feld[x][y];
12019 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12024 int element2 = element;
12025 int graphic2 = graphic;
12027 int element2 = Feld[x][y];
12028 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12030 int last_gfx_frame = GfxFrame[x][y];
12032 if (graphic_info[graphic2].anim_global_sync)
12033 GfxFrame[x][y] = FrameCounter;
12034 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12035 GfxFrame[x][y] = CustomValue[x][y];
12036 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12037 GfxFrame[x][y] = element_info[element2].collect_score;
12038 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12039 GfxFrame[x][y] = ChangeDelay[x][y];
12041 if (redraw && GfxFrame[x][y] != last_gfx_frame)
12042 DrawLevelGraphicAnimation(x, y, graphic2);
12045 ResetGfxFrame(x, y, TRUE);
12049 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12050 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12051 ResetRandomAnimationValue(x, y);
12055 SetRandomAnimationValue(x, y);
12059 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12062 #endif // -------------------- !!! TEST ONLY !!! --------------------
12065 debug_print_timestamp(0, "- time for TEST loop: -->");
12068 SCAN_PLAYFIELD(x, y)
12070 element = Feld[x][y];
12071 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12073 ResetGfxFrame(x, y, TRUE);
12075 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12076 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12077 ResetRandomAnimationValue(x, y);
12079 SetRandomAnimationValue(x, y);
12081 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12083 if (IS_INACTIVE(element))
12085 if (IS_ANIMATED(graphic))
12086 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12091 /* this may take place after moving, so 'element' may have changed */
12092 if (IS_CHANGING(x, y) &&
12093 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12095 int page = element_info[element].event_page_nr[CE_DELAY];
12098 HandleElementChange(x, y, page);
12100 if (CAN_CHANGE(element))
12101 HandleElementChange(x, y, page);
12103 if (HAS_ACTION(element))
12104 ExecuteCustomElementAction(x, y, element, page);
12107 element = Feld[x][y];
12108 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12111 #if 0 // ---------------------------------------------------------------------
12113 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12117 element = Feld[x][y];
12118 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12120 if (IS_ANIMATED(graphic) &&
12121 !IS_MOVING(x, y) &&
12123 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12125 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12126 DrawTwinkleOnField(x, y);
12128 else if (IS_MOVING(x, y))
12129 ContinueMoving(x, y);
12136 case EL_EM_EXIT_OPEN:
12137 case EL_SP_EXIT_OPEN:
12138 case EL_STEEL_EXIT_OPEN:
12139 case EL_EM_STEEL_EXIT_OPEN:
12140 case EL_SP_TERMINAL:
12141 case EL_SP_TERMINAL_ACTIVE:
12142 case EL_EXTRA_TIME:
12143 case EL_SHIELD_NORMAL:
12144 case EL_SHIELD_DEADLY:
12145 if (IS_ANIMATED(graphic))
12146 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12149 case EL_DYNAMITE_ACTIVE:
12150 case EL_EM_DYNAMITE_ACTIVE:
12151 case EL_DYNABOMB_PLAYER_1_ACTIVE:
12152 case EL_DYNABOMB_PLAYER_2_ACTIVE:
12153 case EL_DYNABOMB_PLAYER_3_ACTIVE:
12154 case EL_DYNABOMB_PLAYER_4_ACTIVE:
12155 case EL_SP_DISK_RED_ACTIVE:
12156 CheckDynamite(x, y);
12159 case EL_AMOEBA_GROWING:
12160 AmoebeWaechst(x, y);
12163 case EL_AMOEBA_SHRINKING:
12164 AmoebaDisappearing(x, y);
12167 #if !USE_NEW_AMOEBA_CODE
12168 case EL_AMOEBA_WET:
12169 case EL_AMOEBA_DRY:
12170 case EL_AMOEBA_FULL:
12172 case EL_EMC_DRIPPER:
12173 AmoebeAbleger(x, y);
12177 case EL_GAME_OF_LIFE:
12182 case EL_EXIT_CLOSED:
12186 case EL_EM_EXIT_CLOSED:
12190 case EL_STEEL_EXIT_CLOSED:
12191 CheckExitSteel(x, y);
12194 case EL_EM_STEEL_EXIT_CLOSED:
12195 CheckExitSteelEM(x, y);
12198 case EL_SP_EXIT_CLOSED:
12202 case EL_EXPANDABLE_WALL_GROWING:
12203 case EL_EXPANDABLE_STEELWALL_GROWING:
12204 MauerWaechst(x, y);
12207 case EL_EXPANDABLE_WALL:
12208 case EL_EXPANDABLE_WALL_HORIZONTAL:
12209 case EL_EXPANDABLE_WALL_VERTICAL:
12210 case EL_EXPANDABLE_WALL_ANY:
12211 case EL_BD_EXPANDABLE_WALL:
12212 MauerAbleger(x, y);
12215 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12216 case EL_EXPANDABLE_STEELWALL_VERTICAL:
12217 case EL_EXPANDABLE_STEELWALL_ANY:
12218 MauerAblegerStahl(x, y);
12222 CheckForDragon(x, y);
12228 case EL_ELEMENT_SNAPPING:
12229 case EL_DIAGONAL_SHRINKING:
12230 case EL_DIAGONAL_GROWING:
12233 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12235 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12240 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12241 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12246 #else // ---------------------------------------------------------------------
12248 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12252 element = Feld[x][y];
12253 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12255 if (IS_ANIMATED(graphic) &&
12256 !IS_MOVING(x, y) &&
12258 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12260 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12261 DrawTwinkleOnField(x, y);
12263 else if ((element == EL_ACID ||
12264 element == EL_EXIT_OPEN ||
12265 element == EL_EM_EXIT_OPEN ||
12266 element == EL_SP_EXIT_OPEN ||
12267 element == EL_STEEL_EXIT_OPEN ||
12268 element == EL_EM_STEEL_EXIT_OPEN ||
12269 element == EL_SP_TERMINAL ||
12270 element == EL_SP_TERMINAL_ACTIVE ||
12271 element == EL_EXTRA_TIME ||
12272 element == EL_SHIELD_NORMAL ||
12273 element == EL_SHIELD_DEADLY) &&
12274 IS_ANIMATED(graphic))
12275 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12276 else if (IS_MOVING(x, y))
12277 ContinueMoving(x, y);
12278 else if (IS_ACTIVE_BOMB(element))
12279 CheckDynamite(x, y);
12280 else if (element == EL_AMOEBA_GROWING)
12281 AmoebeWaechst(x, y);
12282 else if (element == EL_AMOEBA_SHRINKING)
12283 AmoebaDisappearing(x, y);
12285 #if !USE_NEW_AMOEBA_CODE
12286 else if (IS_AMOEBALIVE(element))
12287 AmoebeAbleger(x, y);
12290 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12292 else if (element == EL_EXIT_CLOSED)
12294 else if (element == EL_EM_EXIT_CLOSED)
12296 else if (element == EL_STEEL_EXIT_CLOSED)
12297 CheckExitSteel(x, y);
12298 else if (element == EL_EM_STEEL_EXIT_CLOSED)
12299 CheckExitSteelEM(x, y);
12300 else if (element == EL_SP_EXIT_CLOSED)
12302 else if (element == EL_EXPANDABLE_WALL_GROWING ||
12303 element == EL_EXPANDABLE_STEELWALL_GROWING)
12304 MauerWaechst(x, y);
12305 else if (element == EL_EXPANDABLE_WALL ||
12306 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12307 element == EL_EXPANDABLE_WALL_VERTICAL ||
12308 element == EL_EXPANDABLE_WALL_ANY ||
12309 element == EL_BD_EXPANDABLE_WALL)
12310 MauerAbleger(x, y);
12311 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12312 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12313 element == EL_EXPANDABLE_STEELWALL_ANY)
12314 MauerAblegerStahl(x, y);
12315 else if (element == EL_FLAMES)
12316 CheckForDragon(x, y);
12317 else if (element == EL_EXPLOSION)
12318 ; /* drawing of correct explosion animation is handled separately */
12319 else if (element == EL_ELEMENT_SNAPPING ||
12320 element == EL_DIAGONAL_SHRINKING ||
12321 element == EL_DIAGONAL_GROWING)
12323 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12325 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12327 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12328 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12330 #endif // ---------------------------------------------------------------------
12332 if (IS_BELT_ACTIVE(element))
12333 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12335 if (game.magic_wall_active)
12337 int jx = local_player->jx, jy = local_player->jy;
12339 /* play the element sound at the position nearest to the player */
12340 if ((element == EL_MAGIC_WALL_FULL ||
12341 element == EL_MAGIC_WALL_ACTIVE ||
12342 element == EL_MAGIC_WALL_EMPTYING ||
12343 element == EL_BD_MAGIC_WALL_FULL ||
12344 element == EL_BD_MAGIC_WALL_ACTIVE ||
12345 element == EL_BD_MAGIC_WALL_EMPTYING ||
12346 element == EL_DC_MAGIC_WALL_FULL ||
12347 element == EL_DC_MAGIC_WALL_ACTIVE ||
12348 element == EL_DC_MAGIC_WALL_EMPTYING) &&
12349 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12358 debug_print_timestamp(0, "- time for MAIN loop: -->");
12361 #if USE_NEW_AMOEBA_CODE
12362 /* new experimental amoeba growth stuff */
12363 if (!(FrameCounter % 8))
12365 static unsigned long random = 1684108901;
12367 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12369 x = RND(lev_fieldx);
12370 y = RND(lev_fieldy);
12371 element = Feld[x][y];
12373 if (!IS_PLAYER(x,y) &&
12374 (element == EL_EMPTY ||
12375 CAN_GROW_INTO(element) ||
12376 element == EL_QUICKSAND_EMPTY ||
12377 element == EL_QUICKSAND_FAST_EMPTY ||
12378 element == EL_ACID_SPLASH_LEFT ||
12379 element == EL_ACID_SPLASH_RIGHT))
12381 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12382 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12383 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12384 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12385 Feld[x][y] = EL_AMOEBA_DROP;
12388 random = random * 129 + 1;
12394 if (game.explosions_delayed)
12397 game.explosions_delayed = FALSE;
12399 SCAN_PLAYFIELD(x, y)
12401 element = Feld[x][y];
12403 if (ExplodeField[x][y])
12404 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12405 else if (element == EL_EXPLOSION)
12406 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12408 ExplodeField[x][y] = EX_TYPE_NONE;
12411 game.explosions_delayed = TRUE;
12414 if (game.magic_wall_active)
12416 if (!(game.magic_wall_time_left % 4))
12418 int element = Feld[magic_wall_x][magic_wall_y];
12420 if (element == EL_BD_MAGIC_WALL_FULL ||
12421 element == EL_BD_MAGIC_WALL_ACTIVE ||
12422 element == EL_BD_MAGIC_WALL_EMPTYING)
12423 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12424 else if (element == EL_DC_MAGIC_WALL_FULL ||
12425 element == EL_DC_MAGIC_WALL_ACTIVE ||
12426 element == EL_DC_MAGIC_WALL_EMPTYING)
12427 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12429 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12432 if (game.magic_wall_time_left > 0)
12434 game.magic_wall_time_left--;
12436 if (!game.magic_wall_time_left)
12438 SCAN_PLAYFIELD(x, y)
12440 element = Feld[x][y];
12442 if (element == EL_MAGIC_WALL_ACTIVE ||
12443 element == EL_MAGIC_WALL_FULL)
12445 Feld[x][y] = EL_MAGIC_WALL_DEAD;
12446 DrawLevelField(x, y);
12448 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12449 element == EL_BD_MAGIC_WALL_FULL)
12451 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12452 DrawLevelField(x, y);
12454 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12455 element == EL_DC_MAGIC_WALL_FULL)
12457 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12458 DrawLevelField(x, y);
12462 game.magic_wall_active = FALSE;
12467 if (game.light_time_left > 0)
12469 game.light_time_left--;
12471 if (game.light_time_left == 0)
12472 RedrawAllLightSwitchesAndInvisibleElements();
12475 if (game.timegate_time_left > 0)
12477 game.timegate_time_left--;
12479 if (game.timegate_time_left == 0)
12480 CloseAllOpenTimegates();
12483 if (game.lenses_time_left > 0)
12485 game.lenses_time_left--;
12487 if (game.lenses_time_left == 0)
12488 RedrawAllInvisibleElementsForLenses();
12491 if (game.magnify_time_left > 0)
12493 game.magnify_time_left--;
12495 if (game.magnify_time_left == 0)
12496 RedrawAllInvisibleElementsForMagnifier();
12499 for (i = 0; i < MAX_PLAYERS; i++)
12501 struct PlayerInfo *player = &stored_player[i];
12503 if (SHIELD_ON(player))
12505 if (player->shield_deadly_time_left)
12506 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12507 else if (player->shield_normal_time_left)
12508 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12515 PlayAllPlayersSound();
12517 if (options.debug) /* calculate frames per second */
12519 static unsigned long fps_counter = 0;
12520 static int fps_frames = 0;
12521 unsigned long fps_delay_ms = Counter() - fps_counter;
12525 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
12527 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12530 fps_counter = Counter();
12533 redraw_mask |= REDRAW_FPS;
12536 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12538 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12540 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12542 local_player->show_envelope = 0;
12546 debug_print_timestamp(0, "stop main loop profiling ");
12547 printf("----------------------------------------------------------\n");
12550 /* use random number generator in every frame to make it less predictable */
12551 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12555 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12557 int min_x = x, min_y = y, max_x = x, max_y = y;
12560 for (i = 0; i < MAX_PLAYERS; i++)
12562 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12564 if (!stored_player[i].active || &stored_player[i] == player)
12567 min_x = MIN(min_x, jx);
12568 min_y = MIN(min_y, jy);
12569 max_x = MAX(max_x, jx);
12570 max_y = MAX(max_y, jy);
12573 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12576 static boolean AllPlayersInVisibleScreen()
12580 for (i = 0; i < MAX_PLAYERS; i++)
12582 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12584 if (!stored_player[i].active)
12587 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12594 void ScrollLevel(int dx, int dy)
12597 static Bitmap *bitmap_db_field2 = NULL;
12598 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12605 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
12606 /* only horizontal XOR vertical scroll direction allowed */
12607 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
12612 if (bitmap_db_field2 == NULL)
12613 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
12615 /* needed when blitting directly to same bitmap -- should not be needed with
12616 recent SDL libraries, but apparently does not work in 1.2.11 directly */
12617 BlitBitmap(drawto_field, bitmap_db_field2,
12618 FX + TILEX * (dx == -1) - softscroll_offset,
12619 FY + TILEY * (dy == -1) - softscroll_offset,
12620 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12621 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12622 FX + TILEX * (dx == 1) - softscroll_offset,
12623 FY + TILEY * (dy == 1) - softscroll_offset);
12624 BlitBitmap(bitmap_db_field2, drawto_field,
12625 FX + TILEX * (dx == 1) - softscroll_offset,
12626 FY + TILEY * (dy == 1) - softscroll_offset,
12627 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12628 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12629 FX + TILEX * (dx == 1) - softscroll_offset,
12630 FY + TILEY * (dy == 1) - softscroll_offset);
12635 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
12636 int xsize = (BX2 - BX1 + 1);
12637 int ysize = (BY2 - BY1 + 1);
12638 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
12639 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
12640 int step = (start < end ? +1 : -1);
12642 for (i = start; i != end; i += step)
12644 BlitBitmap(drawto_field, drawto_field,
12645 FX + TILEX * (dx != 0 ? i + step : 0),
12646 FY + TILEY * (dy != 0 ? i + step : 0),
12647 TILEX * (dx != 0 ? 1 : xsize),
12648 TILEY * (dy != 0 ? 1 : ysize),
12649 FX + TILEX * (dx != 0 ? i : 0),
12650 FY + TILEY * (dy != 0 ? i : 0));
12655 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12657 BlitBitmap(drawto_field, drawto_field,
12658 FX + TILEX * (dx == -1) - softscroll_offset,
12659 FY + TILEY * (dy == -1) - softscroll_offset,
12660 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12661 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12662 FX + TILEX * (dx == 1) - softscroll_offset,
12663 FY + TILEY * (dy == 1) - softscroll_offset);
12669 x = (dx == 1 ? BX1 : BX2);
12670 for (y = BY1; y <= BY2; y++)
12671 DrawScreenField(x, y);
12676 y = (dy == 1 ? BY1 : BY2);
12677 for (x = BX1; x <= BX2; x++)
12678 DrawScreenField(x, y);
12681 redraw_mask |= REDRAW_FIELD;
12684 static boolean canFallDown(struct PlayerInfo *player)
12686 int jx = player->jx, jy = player->jy;
12688 return (IN_LEV_FIELD(jx, jy + 1) &&
12689 (IS_FREE(jx, jy + 1) ||
12690 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12691 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12692 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12695 static boolean canPassField(int x, int y, int move_dir)
12697 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12698 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12699 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12700 int nextx = x + dx;
12701 int nexty = y + dy;
12702 int element = Feld[x][y];
12704 return (IS_PASSABLE_FROM(element, opposite_dir) &&
12705 !CAN_MOVE(element) &&
12706 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12707 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12708 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12711 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12713 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12714 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12715 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12719 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12720 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12721 (IS_DIGGABLE(Feld[newx][newy]) ||
12722 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12723 canPassField(newx, newy, move_dir)));
12726 static void CheckGravityMovement(struct PlayerInfo *player)
12728 #if USE_PLAYER_GRAVITY
12729 if (player->gravity && !player->programmed_action)
12731 if (game.gravity && !player->programmed_action)
12734 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12735 int move_dir_vertical = player->effective_action & MV_VERTICAL;
12736 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12737 int jx = player->jx, jy = player->jy;
12738 boolean player_is_moving_to_valid_field =
12739 (!player_is_snapping &&
12740 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12741 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12742 boolean player_can_fall_down = canFallDown(player);
12744 if (player_can_fall_down &&
12745 !player_is_moving_to_valid_field)
12746 player->programmed_action = MV_DOWN;
12750 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12752 return CheckGravityMovement(player);
12754 #if USE_PLAYER_GRAVITY
12755 if (player->gravity && !player->programmed_action)
12757 if (game.gravity && !player->programmed_action)
12760 int jx = player->jx, jy = player->jy;
12761 boolean field_under_player_is_free =
12762 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12763 boolean player_is_standing_on_valid_field =
12764 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12765 (IS_WALKABLE(Feld[jx][jy]) &&
12766 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12768 if (field_under_player_is_free && !player_is_standing_on_valid_field)
12769 player->programmed_action = MV_DOWN;
12774 MovePlayerOneStep()
12775 -----------------------------------------------------------------------------
12776 dx, dy: direction (non-diagonal) to try to move the player to
12777 real_dx, real_dy: direction as read from input device (can be diagonal)
12780 boolean MovePlayerOneStep(struct PlayerInfo *player,
12781 int dx, int dy, int real_dx, int real_dy)
12783 int jx = player->jx, jy = player->jy;
12784 int new_jx = jx + dx, new_jy = jy + dy;
12785 #if !USE_FIXED_DONT_RUN_INTO
12789 boolean player_can_move = !player->cannot_move;
12791 if (!player->active || (!dx && !dy))
12792 return MP_NO_ACTION;
12794 player->MovDir = (dx < 0 ? MV_LEFT :
12795 dx > 0 ? MV_RIGHT :
12797 dy > 0 ? MV_DOWN : MV_NONE);
12799 if (!IN_LEV_FIELD(new_jx, new_jy))
12800 return MP_NO_ACTION;
12802 if (!player_can_move)
12804 if (player->MovPos == 0)
12806 player->is_moving = FALSE;
12807 player->is_digging = FALSE;
12808 player->is_collecting = FALSE;
12809 player->is_snapping = FALSE;
12810 player->is_pushing = FALSE;
12815 if (!options.network && game.centered_player_nr == -1 &&
12816 !AllPlayersInSight(player, new_jx, new_jy))
12817 return MP_NO_ACTION;
12819 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
12820 return MP_NO_ACTION;
12823 #if !USE_FIXED_DONT_RUN_INTO
12824 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
12826 /* (moved to DigField()) */
12827 if (player_can_move && DONT_RUN_INTO(element))
12829 if (element == EL_ACID && dx == 0 && dy == 1)
12831 SplashAcid(new_jx, new_jy);
12832 Feld[jx][jy] = EL_PLAYER_1;
12833 InitMovingField(jx, jy, MV_DOWN);
12834 Store[jx][jy] = EL_ACID;
12835 ContinueMoving(jx, jy);
12836 BuryPlayer(player);
12839 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12845 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12846 if (can_move != MP_MOVING)
12849 /* check if DigField() has caused relocation of the player */
12850 if (player->jx != jx || player->jy != jy)
12851 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12853 StorePlayer[jx][jy] = 0;
12854 player->last_jx = jx;
12855 player->last_jy = jy;
12856 player->jx = new_jx;
12857 player->jy = new_jy;
12858 StorePlayer[new_jx][new_jy] = player->element_nr;
12860 if (player->move_delay_value_next != -1)
12862 player->move_delay_value = player->move_delay_value_next;
12863 player->move_delay_value_next = -1;
12867 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12869 player->step_counter++;
12871 PlayerVisit[jx][jy] = FrameCounter;
12873 #if USE_UFAST_PLAYER_EXIT_BUGFIX
12874 player->is_moving = TRUE;
12878 /* should better be called in MovePlayer(), but this breaks some tapes */
12879 ScrollPlayer(player, SCROLL_INIT);
12885 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12887 int jx = player->jx, jy = player->jy;
12888 int old_jx = jx, old_jy = jy;
12889 int moved = MP_NO_ACTION;
12891 if (!player->active)
12896 if (player->MovPos == 0)
12898 player->is_moving = FALSE;
12899 player->is_digging = FALSE;
12900 player->is_collecting = FALSE;
12901 player->is_snapping = FALSE;
12902 player->is_pushing = FALSE;
12908 if (player->move_delay > 0)
12911 player->move_delay = -1; /* set to "uninitialized" value */
12913 /* store if player is automatically moved to next field */
12914 player->is_auto_moving = (player->programmed_action != MV_NONE);
12916 /* remove the last programmed player action */
12917 player->programmed_action = 0;
12919 if (player->MovPos)
12921 /* should only happen if pre-1.2 tape recordings are played */
12922 /* this is only for backward compatibility */
12924 int original_move_delay_value = player->move_delay_value;
12927 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
12931 /* scroll remaining steps with finest movement resolution */
12932 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12934 while (player->MovPos)
12936 ScrollPlayer(player, SCROLL_GO_ON);
12937 ScrollScreen(NULL, SCROLL_GO_ON);
12939 AdvanceFrameAndPlayerCounters(player->index_nr);
12945 player->move_delay_value = original_move_delay_value;
12948 player->is_active = FALSE;
12950 if (player->last_move_dir & MV_HORIZONTAL)
12952 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12953 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12957 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12958 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12961 #if USE_FIXED_BORDER_RUNNING_GFX
12962 if (!moved && !player->is_active)
12964 player->is_moving = FALSE;
12965 player->is_digging = FALSE;
12966 player->is_collecting = FALSE;
12967 player->is_snapping = FALSE;
12968 player->is_pushing = FALSE;
12976 if (moved & MP_MOVING && !ScreenMovPos &&
12977 (player->index_nr == game.centered_player_nr ||
12978 game.centered_player_nr == -1))
12980 if (moved & MP_MOVING && !ScreenMovPos &&
12981 (player == local_player || !options.network))
12984 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12985 int offset = game.scroll_delay_value;
12987 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12989 /* actual player has left the screen -- scroll in that direction */
12990 if (jx != old_jx) /* player has moved horizontally */
12991 scroll_x += (jx - old_jx);
12992 else /* player has moved vertically */
12993 scroll_y += (jy - old_jy);
12997 if (jx != old_jx) /* player has moved horizontally */
12999 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
13000 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13001 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13003 /* don't scroll over playfield boundaries */
13004 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13005 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13007 /* don't scroll more than one field at a time */
13008 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13010 /* don't scroll against the player's moving direction */
13011 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
13012 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13013 scroll_x = old_scroll_x;
13015 else /* player has moved vertically */
13017 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
13018 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13019 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13021 /* don't scroll over playfield boundaries */
13022 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13023 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13025 /* don't scroll more than one field at a time */
13026 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13028 /* don't scroll against the player's moving direction */
13029 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
13030 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13031 scroll_y = old_scroll_y;
13035 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13038 if (!options.network && game.centered_player_nr == -1 &&
13039 !AllPlayersInVisibleScreen())
13041 scroll_x = old_scroll_x;
13042 scroll_y = old_scroll_y;
13046 if (!options.network && !AllPlayersInVisibleScreen())
13048 scroll_x = old_scroll_x;
13049 scroll_y = old_scroll_y;
13054 ScrollScreen(player, SCROLL_INIT);
13055 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13060 player->StepFrame = 0;
13062 if (moved & MP_MOVING)
13064 if (old_jx != jx && old_jy == jy)
13065 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13066 else if (old_jx == jx && old_jy != jy)
13067 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13069 DrawLevelField(jx, jy); /* for "crumbled sand" */
13071 player->last_move_dir = player->MovDir;
13072 player->is_moving = TRUE;
13073 player->is_snapping = FALSE;
13074 player->is_switching = FALSE;
13075 player->is_dropping = FALSE;
13076 player->is_dropping_pressed = FALSE;
13077 player->drop_pressed_delay = 0;
13080 /* should better be called here than above, but this breaks some tapes */
13081 ScrollPlayer(player, SCROLL_INIT);
13086 CheckGravityMovementWhenNotMoving(player);
13088 player->is_moving = FALSE;
13090 /* at this point, the player is allowed to move, but cannot move right now
13091 (e.g. because of something blocking the way) -- ensure that the player
13092 is also allowed to move in the next frame (in old versions before 3.1.1,
13093 the player was forced to wait again for eight frames before next try) */
13095 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13096 player->move_delay = 0; /* allow direct movement in the next frame */
13099 if (player->move_delay == -1) /* not yet initialized by DigField() */
13100 player->move_delay = player->move_delay_value;
13102 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13104 TestIfPlayerTouchesBadThing(jx, jy);
13105 TestIfPlayerTouchesCustomElement(jx, jy);
13108 if (!player->active)
13109 RemovePlayer(player);
13114 void ScrollPlayer(struct PlayerInfo *player, int mode)
13116 int jx = player->jx, jy = player->jy;
13117 int last_jx = player->last_jx, last_jy = player->last_jy;
13118 int move_stepsize = TILEX / player->move_delay_value;
13120 #if USE_NEW_PLAYER_SPEED
13121 if (!player->active)
13124 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
13127 if (!player->active || player->MovPos == 0)
13131 if (mode == SCROLL_INIT)
13133 player->actual_frame_counter = FrameCounter;
13134 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13136 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13137 Feld[last_jx][last_jy] == EL_EMPTY)
13139 int last_field_block_delay = 0; /* start with no blocking at all */
13140 int block_delay_adjustment = player->block_delay_adjustment;
13142 /* if player blocks last field, add delay for exactly one move */
13143 if (player->block_last_field)
13145 last_field_block_delay += player->move_delay_value;
13147 /* when blocking enabled, prevent moving up despite gravity */
13148 #if USE_PLAYER_GRAVITY
13149 if (player->gravity && player->MovDir == MV_UP)
13150 block_delay_adjustment = -1;
13152 if (game.gravity && player->MovDir == MV_UP)
13153 block_delay_adjustment = -1;
13157 /* add block delay adjustment (also possible when not blocking) */
13158 last_field_block_delay += block_delay_adjustment;
13160 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13161 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13164 #if USE_NEW_PLAYER_SPEED
13165 if (player->MovPos != 0) /* player has not yet reached destination */
13171 else if (!FrameReached(&player->actual_frame_counter, 1))
13174 #if USE_NEW_PLAYER_SPEED
13175 if (player->MovPos != 0)
13177 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13178 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13180 /* before DrawPlayer() to draw correct player graphic for this case */
13181 if (player->MovPos == 0)
13182 CheckGravityMovement(player);
13185 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13186 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13188 /* before DrawPlayer() to draw correct player graphic for this case */
13189 if (player->MovPos == 0)
13190 CheckGravityMovement(player);
13193 if (player->MovPos == 0) /* player reached destination field */
13195 if (player->move_delay_reset_counter > 0)
13197 player->move_delay_reset_counter--;
13199 if (player->move_delay_reset_counter == 0)
13201 /* continue with normal speed after quickly moving through gate */
13202 HALVE_PLAYER_SPEED(player);
13204 /* be able to make the next move without delay */
13205 player->move_delay = 0;
13209 player->last_jx = jx;
13210 player->last_jy = jy;
13212 if (Feld[jx][jy] == EL_EXIT_OPEN ||
13213 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13214 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13215 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13216 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13217 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
13219 DrawPlayer(player); /* needed here only to cleanup last field */
13220 RemovePlayer(player);
13222 if (local_player->friends_still_needed == 0 ||
13223 IS_SP_ELEMENT(Feld[jx][jy]))
13224 PlayerWins(player);
13227 /* this breaks one level: "machine", level 000 */
13229 int move_direction = player->MovDir;
13230 int enter_side = MV_DIR_OPPOSITE(move_direction);
13231 int leave_side = move_direction;
13232 int old_jx = last_jx;
13233 int old_jy = last_jy;
13234 int old_element = Feld[old_jx][old_jy];
13235 int new_element = Feld[jx][jy];
13237 if (IS_CUSTOM_ELEMENT(old_element))
13238 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13240 player->index_bit, leave_side);
13242 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13243 CE_PLAYER_LEAVES_X,
13244 player->index_bit, leave_side);
13246 if (IS_CUSTOM_ELEMENT(new_element))
13247 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13248 player->index_bit, enter_side);
13250 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13251 CE_PLAYER_ENTERS_X,
13252 player->index_bit, enter_side);
13254 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13255 CE_MOVE_OF_X, move_direction);
13258 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13260 TestIfPlayerTouchesBadThing(jx, jy);
13261 TestIfPlayerTouchesCustomElement(jx, jy);
13263 /* needed because pushed element has not yet reached its destination,
13264 so it would trigger a change event at its previous field location */
13265 if (!player->is_pushing)
13266 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
13268 if (!player->active)
13269 RemovePlayer(player);
13272 if (!local_player->LevelSolved && level.use_step_counter)
13282 if (TimeLeft <= 10 && setup.time_limit)
13283 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13286 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13288 DisplayGameControlValues();
13290 DrawGameValue_Time(TimeLeft);
13293 if (!TimeLeft && setup.time_limit)
13294 for (i = 0; i < MAX_PLAYERS; i++)
13295 KillPlayer(&stored_player[i]);
13298 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13300 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13302 DisplayGameControlValues();
13305 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13306 DrawGameValue_Time(TimePlayed);
13310 if (tape.single_step && tape.recording && !tape.pausing &&
13311 !player->programmed_action)
13312 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13316 void ScrollScreen(struct PlayerInfo *player, int mode)
13318 static unsigned long screen_frame_counter = 0;
13320 if (mode == SCROLL_INIT)
13322 /* set scrolling step size according to actual player's moving speed */
13323 ScrollStepSize = TILEX / player->move_delay_value;
13325 screen_frame_counter = FrameCounter;
13326 ScreenMovDir = player->MovDir;
13327 ScreenMovPos = player->MovPos;
13328 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13331 else if (!FrameReached(&screen_frame_counter, 1))
13336 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13337 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13338 redraw_mask |= REDRAW_FIELD;
13341 ScreenMovDir = MV_NONE;
13344 void TestIfPlayerTouchesCustomElement(int x, int y)
13346 static int xy[4][2] =
13353 static int trigger_sides[4][2] =
13355 /* center side border side */
13356 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13357 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13358 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13359 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13361 static int touch_dir[4] =
13363 MV_LEFT | MV_RIGHT,
13368 int center_element = Feld[x][y]; /* should always be non-moving! */
13371 for (i = 0; i < NUM_DIRECTIONS; i++)
13373 int xx = x + xy[i][0];
13374 int yy = y + xy[i][1];
13375 int center_side = trigger_sides[i][0];
13376 int border_side = trigger_sides[i][1];
13377 int border_element;
13379 if (!IN_LEV_FIELD(xx, yy))
13382 if (IS_PLAYER(x, y))
13384 struct PlayerInfo *player = PLAYERINFO(x, y);
13386 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13387 border_element = Feld[xx][yy]; /* may be moving! */
13388 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13389 border_element = Feld[xx][yy];
13390 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13391 border_element = MovingOrBlocked2Element(xx, yy);
13393 continue; /* center and border element do not touch */
13395 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13396 player->index_bit, border_side);
13397 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13398 CE_PLAYER_TOUCHES_X,
13399 player->index_bit, border_side);
13401 else if (IS_PLAYER(xx, yy))
13403 struct PlayerInfo *player = PLAYERINFO(xx, yy);
13405 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13407 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13408 continue; /* center and border element do not touch */
13411 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13412 player->index_bit, center_side);
13413 CheckTriggeredElementChangeByPlayer(x, y, center_element,
13414 CE_PLAYER_TOUCHES_X,
13415 player->index_bit, center_side);
13421 #if USE_ELEMENT_TOUCHING_BUGFIX
13423 void TestIfElementTouchesCustomElement(int x, int y)
13425 static int xy[4][2] =
13432 static int trigger_sides[4][2] =
13434 /* center side border side */
13435 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13436 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13437 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13438 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13440 static int touch_dir[4] =
13442 MV_LEFT | MV_RIGHT,
13447 boolean change_center_element = FALSE;
13448 int center_element = Feld[x][y]; /* should always be non-moving! */
13449 int border_element_old[NUM_DIRECTIONS];
13452 for (i = 0; i < NUM_DIRECTIONS; i++)
13454 int xx = x + xy[i][0];
13455 int yy = y + xy[i][1];
13456 int border_element;
13458 border_element_old[i] = -1;
13460 if (!IN_LEV_FIELD(xx, yy))
13463 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13464 border_element = Feld[xx][yy]; /* may be moving! */
13465 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13466 border_element = Feld[xx][yy];
13467 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13468 border_element = MovingOrBlocked2Element(xx, yy);
13470 continue; /* center and border element do not touch */
13472 border_element_old[i] = border_element;
13475 for (i = 0; i < NUM_DIRECTIONS; i++)
13477 int xx = x + xy[i][0];
13478 int yy = y + xy[i][1];
13479 int center_side = trigger_sides[i][0];
13480 int border_element = border_element_old[i];
13482 if (border_element == -1)
13485 /* check for change of border element */
13486 CheckElementChangeBySide(xx, yy, border_element, center_element,
13487 CE_TOUCHING_X, center_side);
13490 for (i = 0; i < NUM_DIRECTIONS; i++)
13492 int border_side = trigger_sides[i][1];
13493 int border_element = border_element_old[i];
13495 if (border_element == -1)
13498 /* check for change of center element (but change it only once) */
13499 if (!change_center_element)
13500 change_center_element =
13501 CheckElementChangeBySide(x, y, center_element, border_element,
13502 CE_TOUCHING_X, border_side);
13508 void TestIfElementTouchesCustomElement_OLD(int x, int y)
13510 static int xy[4][2] =
13517 static int trigger_sides[4][2] =
13519 /* center side border side */
13520 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13521 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13522 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13523 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13525 static int touch_dir[4] =
13527 MV_LEFT | MV_RIGHT,
13532 boolean change_center_element = FALSE;
13533 int center_element = Feld[x][y]; /* should always be non-moving! */
13536 for (i = 0; i < NUM_DIRECTIONS; i++)
13538 int xx = x + xy[i][0];
13539 int yy = y + xy[i][1];
13540 int center_side = trigger_sides[i][0];
13541 int border_side = trigger_sides[i][1];
13542 int border_element;
13544 if (!IN_LEV_FIELD(xx, yy))
13547 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13548 border_element = Feld[xx][yy]; /* may be moving! */
13549 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13550 border_element = Feld[xx][yy];
13551 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13552 border_element = MovingOrBlocked2Element(xx, yy);
13554 continue; /* center and border element do not touch */
13556 /* check for change of center element (but change it only once) */
13557 if (!change_center_element)
13558 change_center_element =
13559 CheckElementChangeBySide(x, y, center_element, border_element,
13560 CE_TOUCHING_X, border_side);
13562 /* check for change of border element */
13563 CheckElementChangeBySide(xx, yy, border_element, center_element,
13564 CE_TOUCHING_X, center_side);
13570 void TestIfElementHitsCustomElement(int x, int y, int direction)
13572 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13573 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
13574 int hitx = x + dx, hity = y + dy;
13575 int hitting_element = Feld[x][y];
13576 int touched_element;
13578 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13581 touched_element = (IN_LEV_FIELD(hitx, hity) ?
13582 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13584 if (IN_LEV_FIELD(hitx, hity))
13586 int opposite_direction = MV_DIR_OPPOSITE(direction);
13587 int hitting_side = direction;
13588 int touched_side = opposite_direction;
13589 boolean object_hit = (!IS_MOVING(hitx, hity) ||
13590 MovDir[hitx][hity] != direction ||
13591 ABS(MovPos[hitx][hity]) <= TILEY / 2);
13597 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13598 CE_HITTING_X, touched_side);
13600 CheckElementChangeBySide(hitx, hity, touched_element,
13601 hitting_element, CE_HIT_BY_X, hitting_side);
13603 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13604 CE_HIT_BY_SOMETHING, opposite_direction);
13608 /* "hitting something" is also true when hitting the playfield border */
13609 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13610 CE_HITTING_SOMETHING, direction);
13614 void TestIfElementSmashesCustomElement(int x, int y, int direction)
13616 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13617 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
13618 int hitx = x + dx, hity = y + dy;
13619 int hitting_element = Feld[x][y];
13620 int touched_element;
13622 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
13623 !IS_FREE(hitx, hity) &&
13624 (!IS_MOVING(hitx, hity) ||
13625 MovDir[hitx][hity] != direction ||
13626 ABS(MovPos[hitx][hity]) <= TILEY / 2));
13629 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13633 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
13637 touched_element = (IN_LEV_FIELD(hitx, hity) ?
13638 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13640 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13641 EP_CAN_SMASH_EVERYTHING, direction);
13643 if (IN_LEV_FIELD(hitx, hity))
13645 int opposite_direction = MV_DIR_OPPOSITE(direction);
13646 int hitting_side = direction;
13647 int touched_side = opposite_direction;
13649 int touched_element = MovingOrBlocked2Element(hitx, hity);
13652 boolean object_hit = (!IS_MOVING(hitx, hity) ||
13653 MovDir[hitx][hity] != direction ||
13654 ABS(MovPos[hitx][hity]) <= TILEY / 2);
13663 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13664 CE_SMASHED_BY_SOMETHING, opposite_direction);
13666 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13667 CE_OTHER_IS_SMASHING, touched_side);
13669 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13670 CE_OTHER_GETS_SMASHED, hitting_side);
13676 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13678 int i, kill_x = -1, kill_y = -1;
13680 int bad_element = -1;
13681 static int test_xy[4][2] =
13688 static int test_dir[4] =
13696 for (i = 0; i < NUM_DIRECTIONS; i++)
13698 int test_x, test_y, test_move_dir, test_element;
13700 test_x = good_x + test_xy[i][0];
13701 test_y = good_y + test_xy[i][1];
13703 if (!IN_LEV_FIELD(test_x, test_y))
13707 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13709 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13711 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13712 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13714 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13715 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
13719 bad_element = test_element;
13725 if (kill_x != -1 || kill_y != -1)
13727 if (IS_PLAYER(good_x, good_y))
13729 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13731 if (player->shield_deadly_time_left > 0 &&
13732 !IS_INDESTRUCTIBLE(bad_element))
13733 Bang(kill_x, kill_y);
13734 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13735 KillPlayer(player);
13738 Bang(good_x, good_y);
13742 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13744 int i, kill_x = -1, kill_y = -1;
13745 int bad_element = Feld[bad_x][bad_y];
13746 static int test_xy[4][2] =
13753 static int touch_dir[4] =
13755 MV_LEFT | MV_RIGHT,
13760 static int test_dir[4] =
13768 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
13771 for (i = 0; i < NUM_DIRECTIONS; i++)
13773 int test_x, test_y, test_move_dir, test_element;
13775 test_x = bad_x + test_xy[i][0];
13776 test_y = bad_y + test_xy[i][1];
13777 if (!IN_LEV_FIELD(test_x, test_y))
13781 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13783 test_element = Feld[test_x][test_y];
13785 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13786 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13788 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
13789 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
13791 /* good thing is player or penguin that does not move away */
13792 if (IS_PLAYER(test_x, test_y))
13794 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13796 if (bad_element == EL_ROBOT && player->is_moving)
13797 continue; /* robot does not kill player if he is moving */
13799 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13801 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13802 continue; /* center and border element do not touch */
13809 else if (test_element == EL_PENGUIN)
13818 if (kill_x != -1 || kill_y != -1)
13820 if (IS_PLAYER(kill_x, kill_y))
13822 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13824 if (player->shield_deadly_time_left > 0 &&
13825 !IS_INDESTRUCTIBLE(bad_element))
13826 Bang(bad_x, bad_y);
13827 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13828 KillPlayer(player);
13831 Bang(kill_x, kill_y);
13835 void TestIfPlayerTouchesBadThing(int x, int y)
13837 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13840 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13842 TestIfGoodThingHitsBadThing(x, y, move_dir);
13845 void TestIfBadThingTouchesPlayer(int x, int y)
13847 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13850 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13852 TestIfBadThingHitsGoodThing(x, y, move_dir);
13855 void TestIfFriendTouchesBadThing(int x, int y)
13857 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13860 void TestIfBadThingTouchesFriend(int x, int y)
13862 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13865 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13867 int i, kill_x = bad_x, kill_y = bad_y;
13868 static int xy[4][2] =
13876 for (i = 0; i < NUM_DIRECTIONS; i++)
13880 x = bad_x + xy[i][0];
13881 y = bad_y + xy[i][1];
13882 if (!IN_LEV_FIELD(x, y))
13885 element = Feld[x][y];
13886 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13887 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13895 if (kill_x != bad_x || kill_y != bad_y)
13896 Bang(bad_x, bad_y);
13899 void KillPlayer(struct PlayerInfo *player)
13901 int jx = player->jx, jy = player->jy;
13903 if (!player->active)
13906 /* the following code was introduced to prevent an infinite loop when calling
13908 -> CheckTriggeredElementChangeExt()
13909 -> ExecuteCustomElementAction()
13911 -> (infinitely repeating the above sequence of function calls)
13912 which occurs when killing the player while having a CE with the setting
13913 "kill player X when explosion of <player X>"; the solution using a new
13914 field "player->killed" was chosen for backwards compatibility, although
13915 clever use of the fields "player->active" etc. would probably also work */
13917 if (player->killed)
13921 player->killed = TRUE;
13923 /* remove accessible field at the player's position */
13924 Feld[jx][jy] = EL_EMPTY;
13926 /* deactivate shield (else Bang()/Explode() would not work right) */
13927 player->shield_normal_time_left = 0;
13928 player->shield_deadly_time_left = 0;
13931 BuryPlayer(player);
13934 static void KillPlayerUnlessEnemyProtected(int x, int y)
13936 if (!PLAYER_ENEMY_PROTECTED(x, y))
13937 KillPlayer(PLAYERINFO(x, y));
13940 static void KillPlayerUnlessExplosionProtected(int x, int y)
13942 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13943 KillPlayer(PLAYERINFO(x, y));
13946 void BuryPlayer(struct PlayerInfo *player)
13948 int jx = player->jx, jy = player->jy;
13950 if (!player->active)
13953 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13954 PlayLevelSound(jx, jy, SND_GAME_LOSING);
13956 player->GameOver = TRUE;
13957 RemovePlayer(player);
13960 void RemovePlayer(struct PlayerInfo *player)
13962 int jx = player->jx, jy = player->jy;
13963 int i, found = FALSE;
13965 player->present = FALSE;
13966 player->active = FALSE;
13968 if (!ExplodeField[jx][jy])
13969 StorePlayer[jx][jy] = 0;
13971 if (player->is_moving)
13972 DrawLevelField(player->last_jx, player->last_jy);
13974 for (i = 0; i < MAX_PLAYERS; i++)
13975 if (stored_player[i].active)
13979 AllPlayersGone = TRUE;
13985 #if USE_NEW_SNAP_DELAY
13986 static void setFieldForSnapping(int x, int y, int element, int direction)
13988 struct ElementInfo *ei = &element_info[element];
13989 int direction_bit = MV_DIR_TO_BIT(direction);
13990 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13991 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13992 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13994 Feld[x][y] = EL_ELEMENT_SNAPPING;
13995 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13997 ResetGfxAnimation(x, y);
13999 GfxElement[x][y] = element;
14000 GfxAction[x][y] = action;
14001 GfxDir[x][y] = direction;
14002 GfxFrame[x][y] = -1;
14007 =============================================================================
14008 checkDiagonalPushing()
14009 -----------------------------------------------------------------------------
14010 check if diagonal input device direction results in pushing of object
14011 (by checking if the alternative direction is walkable, diggable, ...)
14012 =============================================================================
14015 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14016 int x, int y, int real_dx, int real_dy)
14018 int jx, jy, dx, dy, xx, yy;
14020 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
14023 /* diagonal direction: check alternative direction */
14028 xx = jx + (dx == 0 ? real_dx : 0);
14029 yy = jy + (dy == 0 ? real_dy : 0);
14031 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14035 =============================================================================
14037 -----------------------------------------------------------------------------
14038 x, y: field next to player (non-diagonal) to try to dig to
14039 real_dx, real_dy: direction as read from input device (can be diagonal)
14040 =============================================================================
14043 int DigField(struct PlayerInfo *player,
14044 int oldx, int oldy, int x, int y,
14045 int real_dx, int real_dy, int mode)
14047 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14048 boolean player_was_pushing = player->is_pushing;
14049 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14050 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14051 int jx = oldx, jy = oldy;
14052 int dx = x - jx, dy = y - jy;
14053 int nextx = x + dx, nexty = y + dy;
14054 int move_direction = (dx == -1 ? MV_LEFT :
14055 dx == +1 ? MV_RIGHT :
14057 dy == +1 ? MV_DOWN : MV_NONE);
14058 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14059 int dig_side = MV_DIR_OPPOSITE(move_direction);
14060 int old_element = Feld[jx][jy];
14061 #if USE_FIXED_DONT_RUN_INTO
14062 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14068 if (is_player) /* function can also be called by EL_PENGUIN */
14070 if (player->MovPos == 0)
14072 player->is_digging = FALSE;
14073 player->is_collecting = FALSE;
14076 if (player->MovPos == 0) /* last pushing move finished */
14077 player->is_pushing = FALSE;
14079 if (mode == DF_NO_PUSH) /* player just stopped pushing */
14081 player->is_switching = FALSE;
14082 player->push_delay = -1;
14084 return MP_NO_ACTION;
14088 #if !USE_FIXED_DONT_RUN_INTO
14089 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14090 return MP_NO_ACTION;
14093 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14094 old_element = Back[jx][jy];
14096 /* in case of element dropped at player position, check background */
14097 else if (Back[jx][jy] != EL_EMPTY &&
14098 game.engine_version >= VERSION_IDENT(2,2,0,0))
14099 old_element = Back[jx][jy];
14101 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14102 return MP_NO_ACTION; /* field has no opening in this direction */
14104 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14105 return MP_NO_ACTION; /* field has no opening in this direction */
14107 #if USE_FIXED_DONT_RUN_INTO
14108 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14112 Feld[jx][jy] = player->artwork_element;
14113 InitMovingField(jx, jy, MV_DOWN);
14114 Store[jx][jy] = EL_ACID;
14115 ContinueMoving(jx, jy);
14116 BuryPlayer(player);
14118 return MP_DONT_RUN_INTO;
14122 #if USE_FIXED_DONT_RUN_INTO
14123 if (player_can_move && DONT_RUN_INTO(element))
14125 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14127 return MP_DONT_RUN_INTO;
14131 #if USE_FIXED_DONT_RUN_INTO
14132 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14133 return MP_NO_ACTION;
14136 #if !USE_FIXED_DONT_RUN_INTO
14137 element = Feld[x][y];
14140 collect_count = element_info[element].collect_count_initial;
14142 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
14143 return MP_NO_ACTION;
14145 if (game.engine_version < VERSION_IDENT(2,2,0,0))
14146 player_can_move = player_can_move_or_snap;
14148 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14149 game.engine_version >= VERSION_IDENT(2,2,0,0))
14151 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14152 player->index_bit, dig_side);
14153 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14154 player->index_bit, dig_side);
14156 if (element == EL_DC_LANDMINE)
14159 if (Feld[x][y] != element) /* field changed by snapping */
14162 return MP_NO_ACTION;
14165 #if USE_PLAYER_GRAVITY
14166 if (player->gravity && is_player && !player->is_auto_moving &&
14167 canFallDown(player) && move_direction != MV_DOWN &&
14168 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14169 return MP_NO_ACTION; /* player cannot walk here due to gravity */
14171 if (game.gravity && is_player && !player->is_auto_moving &&
14172 canFallDown(player) && move_direction != MV_DOWN &&
14173 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14174 return MP_NO_ACTION; /* player cannot walk here due to gravity */
14177 if (player_can_move &&
14178 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14180 int sound_element = SND_ELEMENT(element);
14181 int sound_action = ACTION_WALKING;
14183 if (IS_RND_GATE(element))
14185 if (!player->key[RND_GATE_NR(element)])
14186 return MP_NO_ACTION;
14188 else if (IS_RND_GATE_GRAY(element))
14190 if (!player->key[RND_GATE_GRAY_NR(element)])
14191 return MP_NO_ACTION;
14193 else if (IS_RND_GATE_GRAY_ACTIVE(element))
14195 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14196 return MP_NO_ACTION;
14198 else if (element == EL_EXIT_OPEN ||
14199 element == EL_EM_EXIT_OPEN ||
14200 element == EL_STEEL_EXIT_OPEN ||
14201 element == EL_EM_STEEL_EXIT_OPEN ||
14202 element == EL_SP_EXIT_OPEN ||
14203 element == EL_SP_EXIT_OPENING)
14205 sound_action = ACTION_PASSING; /* player is passing exit */
14207 else if (element == EL_EMPTY)
14209 sound_action = ACTION_MOVING; /* nothing to walk on */
14212 /* play sound from background or player, whatever is available */
14213 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14214 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14216 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14218 else if (player_can_move &&
14219 IS_PASSABLE(element) && canPassField(x, y, move_direction))
14221 if (!ACCESS_FROM(element, opposite_direction))
14222 return MP_NO_ACTION; /* field not accessible from this direction */
14224 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
14225 return MP_NO_ACTION;
14227 if (IS_EM_GATE(element))
14229 if (!player->key[EM_GATE_NR(element)])
14230 return MP_NO_ACTION;
14232 else if (IS_EM_GATE_GRAY(element))
14234 if (!player->key[EM_GATE_GRAY_NR(element)])
14235 return MP_NO_ACTION;
14237 else if (IS_EM_GATE_GRAY_ACTIVE(element))
14239 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14240 return MP_NO_ACTION;
14242 else if (IS_EMC_GATE(element))
14244 if (!player->key[EMC_GATE_NR(element)])
14245 return MP_NO_ACTION;
14247 else if (IS_EMC_GATE_GRAY(element))
14249 if (!player->key[EMC_GATE_GRAY_NR(element)])
14250 return MP_NO_ACTION;
14252 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14254 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14255 return MP_NO_ACTION;
14257 else if (element == EL_DC_GATE_WHITE ||
14258 element == EL_DC_GATE_WHITE_GRAY ||
14259 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14261 if (player->num_white_keys == 0)
14262 return MP_NO_ACTION;
14264 player->num_white_keys--;
14266 else if (IS_SP_PORT(element))
14268 if (element == EL_SP_GRAVITY_PORT_LEFT ||
14269 element == EL_SP_GRAVITY_PORT_RIGHT ||
14270 element == EL_SP_GRAVITY_PORT_UP ||
14271 element == EL_SP_GRAVITY_PORT_DOWN)
14272 #if USE_PLAYER_GRAVITY
14273 player->gravity = !player->gravity;
14275 game.gravity = !game.gravity;
14277 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14278 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14279 element == EL_SP_GRAVITY_ON_PORT_UP ||
14280 element == EL_SP_GRAVITY_ON_PORT_DOWN)
14281 #if USE_PLAYER_GRAVITY
14282 player->gravity = TRUE;
14284 game.gravity = TRUE;
14286 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14287 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14288 element == EL_SP_GRAVITY_OFF_PORT_UP ||
14289 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14290 #if USE_PLAYER_GRAVITY
14291 player->gravity = FALSE;
14293 game.gravity = FALSE;
14297 /* automatically move to the next field with double speed */
14298 player->programmed_action = move_direction;
14300 if (player->move_delay_reset_counter == 0)
14302 player->move_delay_reset_counter = 2; /* two double speed steps */
14304 DOUBLE_PLAYER_SPEED(player);
14307 PlayLevelSoundAction(x, y, ACTION_PASSING);
14309 else if (player_can_move_or_snap && IS_DIGGABLE(element))
14313 if (mode != DF_SNAP)
14315 GfxElement[x][y] = GFX_ELEMENT(element);
14316 player->is_digging = TRUE;
14319 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14321 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14322 player->index_bit, dig_side);
14324 if (mode == DF_SNAP)
14326 #if USE_NEW_SNAP_DELAY
14327 if (level.block_snap_field)
14328 setFieldForSnapping(x, y, element, move_direction);
14330 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14332 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14335 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14336 player->index_bit, dig_side);
14339 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14343 if (is_player && mode != DF_SNAP)
14345 GfxElement[x][y] = element;
14346 player->is_collecting = TRUE;
14349 if (element == EL_SPEED_PILL)
14351 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14353 else if (element == EL_EXTRA_TIME && level.time > 0)
14355 TimeLeft += level.extra_time;
14358 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14360 DisplayGameControlValues();
14362 DrawGameValue_Time(TimeLeft);
14365 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14367 player->shield_normal_time_left += level.shield_normal_time;
14368 if (element == EL_SHIELD_DEADLY)
14369 player->shield_deadly_time_left += level.shield_deadly_time;
14371 else if (element == EL_DYNAMITE ||
14372 element == EL_EM_DYNAMITE ||
14373 element == EL_SP_DISK_RED)
14375 if (player->inventory_size < MAX_INVENTORY_SIZE)
14376 player->inventory_element[player->inventory_size++] = element;
14378 DrawGameDoorValues();
14380 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14382 player->dynabomb_count++;
14383 player->dynabombs_left++;
14385 else if (element == EL_DYNABOMB_INCREASE_SIZE)
14387 player->dynabomb_size++;
14389 else if (element == EL_DYNABOMB_INCREASE_POWER)
14391 player->dynabomb_xl = TRUE;
14393 else if (IS_KEY(element))
14395 player->key[KEY_NR(element)] = TRUE;
14397 DrawGameDoorValues();
14399 else if (element == EL_DC_KEY_WHITE)
14401 player->num_white_keys++;
14403 /* display white keys? */
14404 /* DrawGameDoorValues(); */
14406 else if (IS_ENVELOPE(element))
14408 player->show_envelope = element;
14410 else if (element == EL_EMC_LENSES)
14412 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14414 RedrawAllInvisibleElementsForLenses();
14416 else if (element == EL_EMC_MAGNIFIER)
14418 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14420 RedrawAllInvisibleElementsForMagnifier();
14422 else if (IS_DROPPABLE(element) ||
14423 IS_THROWABLE(element)) /* can be collected and dropped */
14427 if (collect_count == 0)
14428 player->inventory_infinite_element = element;
14430 for (i = 0; i < collect_count; i++)
14431 if (player->inventory_size < MAX_INVENTORY_SIZE)
14432 player->inventory_element[player->inventory_size++] = element;
14434 DrawGameDoorValues();
14436 else if (collect_count > 0)
14438 local_player->gems_still_needed -= collect_count;
14439 if (local_player->gems_still_needed < 0)
14440 local_player->gems_still_needed = 0;
14443 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
14445 DisplayGameControlValues();
14447 DrawGameValue_Emeralds(local_player->gems_still_needed);
14451 RaiseScoreElement(element);
14452 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14455 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14456 player->index_bit, dig_side);
14458 if (mode == DF_SNAP)
14460 #if USE_NEW_SNAP_DELAY
14461 if (level.block_snap_field)
14462 setFieldForSnapping(x, y, element, move_direction);
14464 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14466 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14469 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14470 player->index_bit, dig_side);
14473 else if (player_can_move_or_snap && IS_PUSHABLE(element))
14475 if (mode == DF_SNAP && element != EL_BD_ROCK)
14476 return MP_NO_ACTION;
14478 if (CAN_FALL(element) && dy)
14479 return MP_NO_ACTION;
14481 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14482 !(element == EL_SPRING && level.use_spring_bug))
14483 return MP_NO_ACTION;
14485 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14486 ((move_direction & MV_VERTICAL &&
14487 ((element_info[element].move_pattern & MV_LEFT &&
14488 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14489 (element_info[element].move_pattern & MV_RIGHT &&
14490 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14491 (move_direction & MV_HORIZONTAL &&
14492 ((element_info[element].move_pattern & MV_UP &&
14493 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14494 (element_info[element].move_pattern & MV_DOWN &&
14495 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14496 return MP_NO_ACTION;
14498 /* do not push elements already moving away faster than player */
14499 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14500 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14501 return MP_NO_ACTION;
14503 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14505 if (player->push_delay_value == -1 || !player_was_pushing)
14506 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14508 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14510 if (player->push_delay_value == -1)
14511 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14513 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14515 if (!player->is_pushing)
14516 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14519 player->is_pushing = TRUE;
14520 player->is_active = TRUE;
14522 if (!(IN_LEV_FIELD(nextx, nexty) &&
14523 (IS_FREE(nextx, nexty) ||
14524 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
14525 IS_SB_ELEMENT(element)))))
14526 return MP_NO_ACTION;
14528 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14529 return MP_NO_ACTION;
14531 if (player->push_delay == -1) /* new pushing; restart delay */
14532 player->push_delay = 0;
14534 if (player->push_delay < player->push_delay_value &&
14535 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14536 element != EL_SPRING && element != EL_BALLOON)
14538 /* make sure that there is no move delay before next try to push */
14539 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14540 player->move_delay = 0;
14542 return MP_NO_ACTION;
14545 if (IS_SB_ELEMENT(element))
14547 if (element == EL_SOKOBAN_FIELD_FULL)
14549 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14550 local_player->sokobanfields_still_needed++;
14553 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14555 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14556 local_player->sokobanfields_still_needed--;
14559 Feld[x][y] = EL_SOKOBAN_OBJECT;
14561 if (Back[x][y] == Back[nextx][nexty])
14562 PlayLevelSoundAction(x, y, ACTION_PUSHING);
14563 else if (Back[x][y] != 0)
14564 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14567 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14570 if (local_player->sokobanfields_still_needed == 0 &&
14571 game.emulation == EMU_SOKOBAN)
14573 PlayerWins(player);
14575 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14579 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14581 InitMovingField(x, y, move_direction);
14582 GfxAction[x][y] = ACTION_PUSHING;
14584 if (mode == DF_SNAP)
14585 ContinueMoving(x, y);
14587 MovPos[x][y] = (dx != 0 ? dx : dy);
14589 Pushed[x][y] = TRUE;
14590 Pushed[nextx][nexty] = TRUE;
14592 if (game.engine_version < VERSION_IDENT(2,2,0,7))
14593 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14595 player->push_delay_value = -1; /* get new value later */
14597 /* check for element change _after_ element has been pushed */
14598 if (game.use_change_when_pushing_bug)
14600 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14601 player->index_bit, dig_side);
14602 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14603 player->index_bit, dig_side);
14606 else if (IS_SWITCHABLE(element))
14608 if (PLAYER_SWITCHING(player, x, y))
14610 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14611 player->index_bit, dig_side);
14616 player->is_switching = TRUE;
14617 player->switch_x = x;
14618 player->switch_y = y;
14620 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14622 if (element == EL_ROBOT_WHEEL)
14624 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14628 game.robot_wheel_active = TRUE;
14630 DrawLevelField(x, y);
14632 else if (element == EL_SP_TERMINAL)
14636 SCAN_PLAYFIELD(xx, yy)
14638 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14640 else if (Feld[xx][yy] == EL_SP_TERMINAL)
14641 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14644 else if (IS_BELT_SWITCH(element))
14646 ToggleBeltSwitch(x, y);
14648 else if (element == EL_SWITCHGATE_SWITCH_UP ||
14649 element == EL_SWITCHGATE_SWITCH_DOWN ||
14650 element == EL_DC_SWITCHGATE_SWITCH_UP ||
14651 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14653 ToggleSwitchgateSwitch(x, y);
14655 else if (element == EL_LIGHT_SWITCH ||
14656 element == EL_LIGHT_SWITCH_ACTIVE)
14658 ToggleLightSwitch(x, y);
14660 else if (element == EL_TIMEGATE_SWITCH ||
14661 element == EL_DC_TIMEGATE_SWITCH)
14663 ActivateTimegateSwitch(x, y);
14665 else if (element == EL_BALLOON_SWITCH_LEFT ||
14666 element == EL_BALLOON_SWITCH_RIGHT ||
14667 element == EL_BALLOON_SWITCH_UP ||
14668 element == EL_BALLOON_SWITCH_DOWN ||
14669 element == EL_BALLOON_SWITCH_NONE ||
14670 element == EL_BALLOON_SWITCH_ANY)
14672 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
14673 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14674 element == EL_BALLOON_SWITCH_UP ? MV_UP :
14675 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
14676 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
14679 else if (element == EL_LAMP)
14681 Feld[x][y] = EL_LAMP_ACTIVE;
14682 local_player->lights_still_needed--;
14684 ResetGfxAnimation(x, y);
14685 DrawLevelField(x, y);
14687 else if (element == EL_TIME_ORB_FULL)
14689 Feld[x][y] = EL_TIME_ORB_EMPTY;
14691 if (level.time > 0 || level.use_time_orb_bug)
14693 TimeLeft += level.time_orb_time;
14696 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14698 DisplayGameControlValues();
14700 DrawGameValue_Time(TimeLeft);
14704 ResetGfxAnimation(x, y);
14705 DrawLevelField(x, y);
14707 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14708 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14712 game.ball_state = !game.ball_state;
14714 SCAN_PLAYFIELD(xx, yy)
14716 int e = Feld[xx][yy];
14718 if (game.ball_state)
14720 if (e == EL_EMC_MAGIC_BALL)
14721 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14722 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14723 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14727 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14728 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14729 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14730 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14735 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14736 player->index_bit, dig_side);
14738 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14739 player->index_bit, dig_side);
14741 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14742 player->index_bit, dig_side);
14748 if (!PLAYER_SWITCHING(player, x, y))
14750 player->is_switching = TRUE;
14751 player->switch_x = x;
14752 player->switch_y = y;
14754 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14755 player->index_bit, dig_side);
14756 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14757 player->index_bit, dig_side);
14759 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14760 player->index_bit, dig_side);
14761 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14762 player->index_bit, dig_side);
14765 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14766 player->index_bit, dig_side);
14767 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14768 player->index_bit, dig_side);
14770 return MP_NO_ACTION;
14773 player->push_delay = -1;
14775 if (is_player) /* function can also be called by EL_PENGUIN */
14777 if (Feld[x][y] != element) /* really digged/collected something */
14779 player->is_collecting = !player->is_digging;
14780 player->is_active = TRUE;
14787 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14789 int jx = player->jx, jy = player->jy;
14790 int x = jx + dx, y = jy + dy;
14791 int snap_direction = (dx == -1 ? MV_LEFT :
14792 dx == +1 ? MV_RIGHT :
14794 dy == +1 ? MV_DOWN : MV_NONE);
14795 boolean can_continue_snapping = (level.continuous_snapping &&
14796 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14798 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14801 if (!player->active || !IN_LEV_FIELD(x, y))
14809 if (player->MovPos == 0)
14810 player->is_pushing = FALSE;
14812 player->is_snapping = FALSE;
14814 if (player->MovPos == 0)
14816 player->is_moving = FALSE;
14817 player->is_digging = FALSE;
14818 player->is_collecting = FALSE;
14824 #if USE_NEW_CONTINUOUS_SNAPPING
14825 /* prevent snapping with already pressed snap key when not allowed */
14826 if (player->is_snapping && !can_continue_snapping)
14829 if (player->is_snapping)
14833 player->MovDir = snap_direction;
14835 if (player->MovPos == 0)
14837 player->is_moving = FALSE;
14838 player->is_digging = FALSE;
14839 player->is_collecting = FALSE;
14842 player->is_dropping = FALSE;
14843 player->is_dropping_pressed = FALSE;
14844 player->drop_pressed_delay = 0;
14846 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14849 player->is_snapping = TRUE;
14850 player->is_active = TRUE;
14852 if (player->MovPos == 0)
14854 player->is_moving = FALSE;
14855 player->is_digging = FALSE;
14856 player->is_collecting = FALSE;
14859 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
14860 DrawLevelField(player->last_jx, player->last_jy);
14862 DrawLevelField(x, y);
14867 boolean DropElement(struct PlayerInfo *player)
14869 int old_element, new_element;
14870 int dropx = player->jx, dropy = player->jy;
14871 int drop_direction = player->MovDir;
14872 int drop_side = drop_direction;
14874 int drop_element = get_next_dropped_element(player);
14876 int drop_element = (player->inventory_size > 0 ?
14877 player->inventory_element[player->inventory_size - 1] :
14878 player->inventory_infinite_element != EL_UNDEFINED ?
14879 player->inventory_infinite_element :
14880 player->dynabombs_left > 0 ?
14881 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
14885 player->is_dropping_pressed = TRUE;
14887 /* do not drop an element on top of another element; when holding drop key
14888 pressed without moving, dropped element must move away before the next
14889 element can be dropped (this is especially important if the next element
14890 is dynamite, which can be placed on background for historical reasons) */
14891 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14894 if (IS_THROWABLE(drop_element))
14896 dropx += GET_DX_FROM_DIR(drop_direction);
14897 dropy += GET_DY_FROM_DIR(drop_direction);
14899 if (!IN_LEV_FIELD(dropx, dropy))
14903 old_element = Feld[dropx][dropy]; /* old element at dropping position */
14904 new_element = drop_element; /* default: no change when dropping */
14906 /* check if player is active, not moving and ready to drop */
14907 if (!player->active || player->MovPos || player->drop_delay > 0)
14910 /* check if player has anything that can be dropped */
14911 if (new_element == EL_UNDEFINED)
14914 /* check if drop key was pressed long enough for EM style dynamite */
14915 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14918 /* check if anything can be dropped at the current position */
14919 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14922 /* collected custom elements can only be dropped on empty fields */
14923 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14926 if (old_element != EL_EMPTY)
14927 Back[dropx][dropy] = old_element; /* store old element on this field */
14929 ResetGfxAnimation(dropx, dropy);
14930 ResetRandomAnimationValue(dropx, dropy);
14932 if (player->inventory_size > 0 ||
14933 player->inventory_infinite_element != EL_UNDEFINED)
14935 if (player->inventory_size > 0)
14937 player->inventory_size--;
14939 DrawGameDoorValues();
14941 if (new_element == EL_DYNAMITE)
14942 new_element = EL_DYNAMITE_ACTIVE;
14943 else if (new_element == EL_EM_DYNAMITE)
14944 new_element = EL_EM_DYNAMITE_ACTIVE;
14945 else if (new_element == EL_SP_DISK_RED)
14946 new_element = EL_SP_DISK_RED_ACTIVE;
14949 Feld[dropx][dropy] = new_element;
14951 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14952 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14953 el2img(Feld[dropx][dropy]), 0);
14955 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14957 /* needed if previous element just changed to "empty" in the last frame */
14958 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14960 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14961 player->index_bit, drop_side);
14962 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14964 player->index_bit, drop_side);
14966 TestIfElementTouchesCustomElement(dropx, dropy);
14968 else /* player is dropping a dyna bomb */
14970 player->dynabombs_left--;
14972 Feld[dropx][dropy] = new_element;
14974 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14975 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14976 el2img(Feld[dropx][dropy]), 0);
14978 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14981 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14982 InitField_WithBug1(dropx, dropy, FALSE);
14984 new_element = Feld[dropx][dropy]; /* element might have changed */
14986 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14987 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14989 int move_direction, nextx, nexty;
14991 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14992 MovDir[dropx][dropy] = drop_direction;
14994 move_direction = MovDir[dropx][dropy];
14995 nextx = dropx + GET_DX_FROM_DIR(move_direction);
14996 nexty = dropy + GET_DY_FROM_DIR(move_direction);
14998 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15000 #if USE_FIX_IMPACT_COLLISION
15001 /* do not cause impact style collision by dropping elements that can fall */
15002 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15004 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15008 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15009 player->is_dropping = TRUE;
15011 player->drop_pressed_delay = 0;
15012 player->is_dropping_pressed = FALSE;
15014 player->drop_x = dropx;
15015 player->drop_y = dropy;
15020 /* ------------------------------------------------------------------------- */
15021 /* game sound playing functions */
15022 /* ------------------------------------------------------------------------- */
15024 static int *loop_sound_frame = NULL;
15025 static int *loop_sound_volume = NULL;
15027 void InitPlayLevelSound()
15029 int num_sounds = getSoundListSize();
15031 checked_free(loop_sound_frame);
15032 checked_free(loop_sound_volume);
15034 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
15035 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15038 static void PlayLevelSound(int x, int y, int nr)
15040 int sx = SCREENX(x), sy = SCREENY(y);
15041 int volume, stereo_position;
15042 int max_distance = 8;
15043 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15045 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15046 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15049 if (!IN_LEV_FIELD(x, y) ||
15050 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15051 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15054 volume = SOUND_MAX_VOLUME;
15056 if (!IN_SCR_FIELD(sx, sy))
15058 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15059 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15061 volume -= volume * (dx > dy ? dx : dy) / max_distance;
15064 stereo_position = (SOUND_MAX_LEFT +
15065 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15066 (SCR_FIELDX + 2 * max_distance));
15068 if (IS_LOOP_SOUND(nr))
15070 /* This assures that quieter loop sounds do not overwrite louder ones,
15071 while restarting sound volume comparison with each new game frame. */
15073 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15076 loop_sound_volume[nr] = volume;
15077 loop_sound_frame[nr] = FrameCounter;
15080 PlaySoundExt(nr, volume, stereo_position, type);
15083 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15085 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15086 x > LEVELX(BX2) ? LEVELX(BX2) : x,
15087 y < LEVELY(BY1) ? LEVELY(BY1) :
15088 y > LEVELY(BY2) ? LEVELY(BY2) : y,
15092 static void PlayLevelSoundAction(int x, int y, int action)
15094 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
15097 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15099 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15101 if (sound_effect != SND_UNDEFINED)
15102 PlayLevelSound(x, y, sound_effect);
15105 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15108 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15110 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15111 PlayLevelSound(x, y, sound_effect);
15114 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15116 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15118 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15119 PlayLevelSound(x, y, sound_effect);
15122 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15124 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15126 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15127 StopSound(sound_effect);
15130 static void PlayLevelMusic()
15132 if (levelset.music[level_nr] != MUS_UNDEFINED)
15133 PlayMusic(levelset.music[level_nr]); /* from config file */
15135 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
15138 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15140 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
15141 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
15142 int x = xx - 1 - offset;
15143 int y = yy - 1 - offset;
15148 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15152 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15156 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15160 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15164 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15168 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15172 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15175 case SAMPLE_android_clone:
15176 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15179 case SAMPLE_android_move:
15180 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15183 case SAMPLE_spring:
15184 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15188 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15192 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15195 case SAMPLE_eater_eat:
15196 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15200 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15203 case SAMPLE_collect:
15204 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15207 case SAMPLE_diamond:
15208 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15211 case SAMPLE_squash:
15212 /* !!! CHECK THIS !!! */
15214 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15216 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15220 case SAMPLE_wonderfall:
15221 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15225 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15229 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15233 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15237 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15241 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15245 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15248 case SAMPLE_wonder:
15249 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15253 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15256 case SAMPLE_exit_open:
15257 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15260 case SAMPLE_exit_leave:
15261 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15264 case SAMPLE_dynamite:
15265 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15269 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15273 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15277 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15281 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15285 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15289 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15293 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15299 void ChangeTime(int value)
15301 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
15305 /* EMC game engine uses value from time counter of RND game engine */
15306 level.native_em_level->lev->time = *time;
15308 DrawGameValue_Time(*time);
15311 void RaiseScore(int value)
15313 /* EMC game engine and RND game engine have separate score counters */
15314 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
15315 &level.native_em_level->lev->score : &local_player->score);
15319 DrawGameValue_Score(*score);
15323 void RaiseScore(int value)
15325 local_player->score += value;
15328 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
15330 DisplayGameControlValues();
15332 DrawGameValue_Score(local_player->score);
15336 void RaiseScoreElement(int element)
15341 case EL_BD_DIAMOND:
15342 case EL_EMERALD_YELLOW:
15343 case EL_EMERALD_RED:
15344 case EL_EMERALD_PURPLE:
15345 case EL_SP_INFOTRON:
15346 RaiseScore(level.score[SC_EMERALD]);
15349 RaiseScore(level.score[SC_DIAMOND]);
15352 RaiseScore(level.score[SC_CRYSTAL]);
15355 RaiseScore(level.score[SC_PEARL]);
15358 case EL_BD_BUTTERFLY:
15359 case EL_SP_ELECTRON:
15360 RaiseScore(level.score[SC_BUG]);
15363 case EL_BD_FIREFLY:
15364 case EL_SP_SNIKSNAK:
15365 RaiseScore(level.score[SC_SPACESHIP]);
15368 case EL_DARK_YAMYAM:
15369 RaiseScore(level.score[SC_YAMYAM]);
15372 RaiseScore(level.score[SC_ROBOT]);
15375 RaiseScore(level.score[SC_PACMAN]);
15378 RaiseScore(level.score[SC_NUT]);
15381 case EL_EM_DYNAMITE:
15382 case EL_SP_DISK_RED:
15383 case EL_DYNABOMB_INCREASE_NUMBER:
15384 case EL_DYNABOMB_INCREASE_SIZE:
15385 case EL_DYNABOMB_INCREASE_POWER:
15386 RaiseScore(level.score[SC_DYNAMITE]);
15388 case EL_SHIELD_NORMAL:
15389 case EL_SHIELD_DEADLY:
15390 RaiseScore(level.score[SC_SHIELD]);
15392 case EL_EXTRA_TIME:
15393 RaiseScore(level.extra_time_score);
15407 case EL_DC_KEY_WHITE:
15408 RaiseScore(level.score[SC_KEY]);
15411 RaiseScore(element_info[element].collect_score);
15416 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15418 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15420 #if defined(NETWORK_AVALIABLE)
15421 if (options.network)
15422 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15431 FadeSkipNextFadeIn();
15433 fading = fading_none;
15437 OpenDoor(DOOR_CLOSE_1);
15440 game_status = GAME_MODE_MAIN;
15443 DrawAndFadeInMainMenu(REDRAW_FIELD);
15451 FadeOut(REDRAW_FIELD);
15454 game_status = GAME_MODE_MAIN;
15456 DrawAndFadeInMainMenu(REDRAW_FIELD);
15460 else /* continue playing the game */
15462 if (tape.playing && tape.deactivate_display)
15463 TapeDeactivateDisplayOff(TRUE);
15465 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15467 if (tape.playing && tape.deactivate_display)
15468 TapeDeactivateDisplayOn();
15472 void RequestQuitGame(boolean ask_if_really_quit)
15474 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15475 boolean skip_request = AllPlayersGone || quick_quit;
15477 RequestQuitGameExt(skip_request, quick_quit,
15478 "Do you really want to quit the game ?");
15482 /* ------------------------------------------------------------------------- */
15483 /* random generator functions */
15484 /* ------------------------------------------------------------------------- */
15486 unsigned int InitEngineRandom_RND(long seed)
15488 game.num_random_calls = 0;
15491 unsigned int rnd_seed = InitEngineRandom(seed);
15493 printf("::: START RND: %d\n", rnd_seed);
15498 return InitEngineRandom(seed);
15504 unsigned int RND(int max)
15508 game.num_random_calls++;
15510 return GetEngineRandom(max);
15517 /* ------------------------------------------------------------------------- */
15518 /* game engine snapshot handling functions */
15519 /* ------------------------------------------------------------------------- */
15521 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
15523 struct EngineSnapshotInfo
15525 /* runtime values for custom element collect score */
15526 int collect_score[NUM_CUSTOM_ELEMENTS];
15528 /* runtime values for group element choice position */
15529 int choice_pos[NUM_GROUP_ELEMENTS];
15531 /* runtime values for belt position animations */
15532 int belt_graphic[4 * NUM_BELT_PARTS];
15533 int belt_anim_mode[4 * NUM_BELT_PARTS];
15536 struct EngineSnapshotNodeInfo
15543 static struct EngineSnapshotInfo engine_snapshot_rnd;
15544 static ListNode *engine_snapshot_list = NULL;
15545 static char *snapshot_level_identifier = NULL;
15546 static int snapshot_level_nr = -1;
15548 void FreeEngineSnapshot()
15550 while (engine_snapshot_list != NULL)
15551 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
15554 setString(&snapshot_level_identifier, NULL);
15555 snapshot_level_nr = -1;
15558 static void SaveEngineSnapshotValues_RND()
15560 static int belt_base_active_element[4] =
15562 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15563 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15564 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15565 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15569 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15571 int element = EL_CUSTOM_START + i;
15573 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15576 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15578 int element = EL_GROUP_START + i;
15580 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15583 for (i = 0; i < 4; i++)
15585 for (j = 0; j < NUM_BELT_PARTS; j++)
15587 int element = belt_base_active_element[i] + j;
15588 int graphic = el2img(element);
15589 int anim_mode = graphic_info[graphic].anim_mode;
15591 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
15592 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
15597 static void LoadEngineSnapshotValues_RND()
15599 unsigned long num_random_calls = game.num_random_calls;
15602 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15604 int element = EL_CUSTOM_START + i;
15606 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15609 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15611 int element = EL_GROUP_START + i;
15613 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15616 for (i = 0; i < 4; i++)
15618 for (j = 0; j < NUM_BELT_PARTS; j++)
15620 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
15621 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
15623 graphic_info[graphic].anim_mode = anim_mode;
15627 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15629 InitRND(tape.random_seed);
15630 for (i = 0; i < num_random_calls; i++)
15634 if (game.num_random_calls != num_random_calls)
15636 Error(ERR_INFO, "number of random calls out of sync");
15637 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15638 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15639 Error(ERR_EXIT, "this should not happen -- please debug");
15643 static void SaveEngineSnapshotBuffer(void *buffer, int size)
15645 struct EngineSnapshotNodeInfo *bi =
15646 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
15648 bi->buffer_orig = buffer;
15649 bi->buffer_copy = checked_malloc(size);
15652 memcpy(bi->buffer_copy, buffer, size);
15654 addNodeToList(&engine_snapshot_list, NULL, bi);
15657 void SaveEngineSnapshot()
15659 FreeEngineSnapshot(); /* free previous snapshot, if needed */
15661 if (level_editor_test_game) /* do not save snapshots from editor */
15664 /* copy some special values to a structure better suited for the snapshot */
15666 SaveEngineSnapshotValues_RND();
15667 SaveEngineSnapshotValues_EM();
15669 /* save values stored in special snapshot structure */
15671 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15672 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15674 /* save further RND engine values */
15676 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
15677 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
15678 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
15680 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
15681 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
15682 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
15683 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
15685 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15686 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15687 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15688 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15689 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15691 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15692 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15693 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15695 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15697 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15699 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15700 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15702 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
15703 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
15704 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
15705 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15706 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15707 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15708 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15709 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
15710 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
15711 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15712 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
15713 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15714 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15715 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15716 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15717 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15718 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
15719 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
15721 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15722 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15724 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15725 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15726 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15728 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15729 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15731 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15732 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15733 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15734 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15735 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15737 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15738 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15740 /* save level identification information */
15742 setString(&snapshot_level_identifier, leveldir_current->identifier);
15743 snapshot_level_nr = level_nr;
15746 ListNode *node = engine_snapshot_list;
15749 while (node != NULL)
15751 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15756 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15760 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
15762 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
15765 void LoadEngineSnapshot()
15767 ListNode *node = engine_snapshot_list;
15769 if (engine_snapshot_list == NULL)
15772 while (node != NULL)
15774 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
15779 /* restore special values from snapshot structure */
15781 LoadEngineSnapshotValues_RND();
15782 LoadEngineSnapshotValues_EM();
15785 boolean CheckEngineSnapshot()
15787 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15788 snapshot_level_nr == level_nr);
15792 /* ---------- new game button stuff ---------------------------------------- */
15794 /* graphic position values for game buttons */
15795 #define GAME_BUTTON_XSIZE 30
15796 #define GAME_BUTTON_YSIZE 30
15797 #define GAME_BUTTON_XPOS 5
15798 #define GAME_BUTTON_YPOS 215
15799 #define SOUND_BUTTON_XPOS 5
15800 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
15802 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15803 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15804 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15805 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15806 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15807 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15815 } gamebutton_info[NUM_GAME_BUTTONS] =
15819 &game.button.stop.x, &game.button.stop.y,
15820 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
15825 &game.button.pause.x, &game.button.pause.y,
15826 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
15827 GAME_CTRL_ID_PAUSE,
15831 &game.button.play.x, &game.button.play.y,
15832 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
15837 &game.button.sound_music.x, &game.button.sound_music.y,
15838 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
15839 SOUND_CTRL_ID_MUSIC,
15840 "background music on/off"
15843 &game.button.sound_loops.x, &game.button.sound_loops.y,
15844 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
15845 SOUND_CTRL_ID_LOOPS,
15846 "sound loops on/off"
15849 &game.button.sound_simple.x,&game.button.sound_simple.y,
15850 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
15851 SOUND_CTRL_ID_SIMPLE,
15852 "normal sounds on/off"
15856 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
15861 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
15862 GAME_CTRL_ID_PAUSE,
15866 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
15871 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
15872 SOUND_CTRL_ID_MUSIC,
15873 "background music on/off"
15876 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
15877 SOUND_CTRL_ID_LOOPS,
15878 "sound loops on/off"
15881 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
15882 SOUND_CTRL_ID_SIMPLE,
15883 "normal sounds on/off"
15888 void CreateGameButtons()
15892 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15894 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
15895 struct GadgetInfo *gi;
15898 unsigned long event_mask;
15900 int gd_xoffset, gd_yoffset;
15901 int gd_x1, gd_x2, gd_y1, gd_y2;
15904 x = DX + *gamebutton_info[i].x;
15905 y = DY + *gamebutton_info[i].y;
15906 gd_xoffset = gamebutton_info[i].gd_x;
15907 gd_yoffset = gamebutton_info[i].gd_y;
15908 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
15909 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
15911 if (id == GAME_CTRL_ID_STOP ||
15912 id == GAME_CTRL_ID_PAUSE ||
15913 id == GAME_CTRL_ID_PLAY)
15915 button_type = GD_TYPE_NORMAL_BUTTON;
15917 event_mask = GD_EVENT_RELEASED;
15918 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15919 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15923 button_type = GD_TYPE_CHECK_BUTTON;
15925 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15926 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15927 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15928 event_mask = GD_EVENT_PRESSED;
15929 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
15930 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15933 gi = CreateGadget(GDI_CUSTOM_ID, id,
15934 GDI_INFO_TEXT, gamebutton_info[i].infotext,
15939 GDI_X, DX + gd_xoffset,
15940 GDI_Y, DY + gd_yoffset,
15942 GDI_WIDTH, GAME_BUTTON_XSIZE,
15943 GDI_HEIGHT, GAME_BUTTON_YSIZE,
15944 GDI_TYPE, button_type,
15945 GDI_STATE, GD_BUTTON_UNPRESSED,
15946 GDI_CHECKED, checked,
15947 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
15948 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
15949 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
15950 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
15951 GDI_DIRECT_DRAW, FALSE,
15952 GDI_EVENT_MASK, event_mask,
15953 GDI_CALLBACK_ACTION, HandleGameButtons,
15957 Error(ERR_EXIT, "cannot create gadget");
15959 game_gadget[id] = gi;
15963 void FreeGameButtons()
15967 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15968 FreeGadget(game_gadget[i]);
15971 static void MapGameButtons()
15975 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15976 MapGadget(game_gadget[i]);
15979 void UnmapGameButtons()
15983 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15984 UnmapGadget(game_gadget[i]);
15987 void RedrawGameButtons()
15991 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15992 RedrawGadget(game_gadget[i]);
15995 static void HandleGameButtons(struct GadgetInfo *gi)
15997 int id = gi->custom_id;
15999 if (game_status != GAME_MODE_PLAYING)
16004 case GAME_CTRL_ID_STOP:
16008 RequestQuitGame(TRUE);
16011 case GAME_CTRL_ID_PAUSE:
16012 if (options.network)
16014 #if defined(NETWORK_AVALIABLE)
16016 SendToServer_ContinuePlaying();
16018 SendToServer_PausePlaying();
16022 TapeTogglePause(TAPE_TOGGLE_MANUAL);
16025 case GAME_CTRL_ID_PLAY:
16028 #if defined(NETWORK_AVALIABLE)
16029 if (options.network)
16030 SendToServer_ContinuePlaying();
16034 tape.pausing = FALSE;
16035 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16040 case SOUND_CTRL_ID_MUSIC:
16041 if (setup.sound_music)
16043 setup.sound_music = FALSE;
16046 else if (audio.music_available)
16048 setup.sound = setup.sound_music = TRUE;
16050 SetAudioMode(setup.sound);
16056 case SOUND_CTRL_ID_LOOPS:
16057 if (setup.sound_loops)
16058 setup.sound_loops = FALSE;
16059 else if (audio.loops_available)
16061 setup.sound = setup.sound_loops = TRUE;
16062 SetAudioMode(setup.sound);
16066 case SOUND_CTRL_ID_SIMPLE:
16067 if (setup.sound_simple)
16068 setup.sound_simple = FALSE;
16069 else if (audio.sound_available)
16071 setup.sound = setup.sound_simple = TRUE;
16072 SetAudioMode(setup.sound);