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 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1037 static boolean DigFieldByCE(int, int, int);
1038 static boolean SnapField(struct PlayerInfo *, int, int);
1039 static boolean DropElement(struct PlayerInfo *);
1041 static void InitBeltMovement(void);
1042 static void CloseAllOpenTimegates(void);
1043 static void CheckGravityMovement(struct PlayerInfo *);
1044 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1045 static void KillPlayerUnlessEnemyProtected(int, int);
1046 static void KillPlayerUnlessExplosionProtected(int, int);
1048 static void TestIfPlayerTouchesCustomElement(int, int);
1049 static void TestIfElementTouchesCustomElement(int, int);
1050 static void TestIfElementHitsCustomElement(int, int, int);
1052 static void TestIfElementSmashesCustomElement(int, int, int);
1055 static void HandleElementChange(int, int, int);
1056 static void ExecuteCustomElementAction(int, int, int, int);
1057 static boolean ChangeElement(int, int, int, int);
1059 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1060 #define CheckTriggeredElementChange(x, y, e, ev) \
1061 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1062 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
1063 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1064 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
1065 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1066 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
1067 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1069 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1070 #define CheckElementChange(x, y, e, te, ev) \
1071 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1072 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
1073 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1074 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
1075 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1077 static void PlayLevelSound(int, int, int);
1078 static void PlayLevelSoundNearest(int, int, int);
1079 static void PlayLevelSoundAction(int, int, int);
1080 static void PlayLevelSoundElementAction(int, int, int, int);
1081 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1082 static void PlayLevelSoundActionIfLoop(int, int, int);
1083 static void StopLevelSoundActionIfLoop(int, int, int);
1084 static void PlayLevelMusic();
1086 static void MapGameButtons();
1087 static void HandleGameButtons(struct GadgetInfo *);
1089 int AmoebeNachbarNr(int, int);
1090 void AmoebeUmwandeln(int, int);
1091 void ContinueMoving(int, int);
1092 void Bang(int, int);
1093 void InitMovDir(int, int);
1094 void InitAmoebaNr(int, int);
1095 int NewHiScore(void);
1097 void TestIfGoodThingHitsBadThing(int, int, int);
1098 void TestIfBadThingHitsGoodThing(int, int, int);
1099 void TestIfPlayerTouchesBadThing(int, int);
1100 void TestIfPlayerRunsIntoBadThing(int, int, int);
1101 void TestIfBadThingTouchesPlayer(int, int);
1102 void TestIfBadThingRunsIntoPlayer(int, int, int);
1103 void TestIfFriendTouchesBadThing(int, int);
1104 void TestIfBadThingTouchesFriend(int, int);
1105 void TestIfBadThingTouchesOtherBadThing(int, int);
1107 void KillPlayer(struct PlayerInfo *);
1108 void BuryPlayer(struct PlayerInfo *);
1109 void RemovePlayer(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))
8381 if (!DigFieldByCE(newx, newy, element))
8384 int new_element = Feld[newx][newy];
8386 if (!IS_FREE(newx, newy))
8388 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8389 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8392 /* no element can dig solid indestructible elements */
8393 if (IS_INDESTRUCTIBLE(new_element) &&
8394 !IS_DIGGABLE(new_element) &&
8395 !IS_COLLECTIBLE(new_element))
8398 if (AmoebaNr[newx][newy] &&
8399 (new_element == EL_AMOEBA_FULL ||
8400 new_element == EL_BD_AMOEBA ||
8401 new_element == EL_AMOEBA_GROWING))
8403 AmoebaCnt[AmoebaNr[newx][newy]]--;
8404 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8407 if (IS_MOVING(newx, newy))
8408 RemoveMovingField(newx, newy);
8411 RemoveField(newx, newy);
8412 DrawLevelField(newx, newy);
8415 /* if digged element was about to explode, prevent the explosion */
8416 ExplodeField[newx][newy] = EX_TYPE_NONE;
8418 PlayLevelSoundAction(x, y, action);
8421 Store[newx][newy] = EL_EMPTY;
8424 /* this makes it possible to leave the removed element again */
8425 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8426 Store[newx][newy] = new_element;
8428 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8430 int move_leave_element = element_info[element].move_leave_element;
8432 /* this makes it possible to leave the removed element again */
8433 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8434 new_element : move_leave_element);
8440 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8442 RunnerVisit[x][y] = FrameCounter;
8443 PlayerVisit[x][y] /= 8; /* expire player visit path */
8446 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8448 if (!IS_FREE(newx, newy))
8450 if (IS_PLAYER(x, y))
8451 DrawPlayerField(x, y);
8453 DrawLevelField(x, y);
8459 boolean wanna_flame = !RND(10);
8460 int dx = newx - x, dy = newy - y;
8461 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8462 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8463 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8464 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8465 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8466 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8469 IS_CLASSIC_ENEMY(element1) ||
8470 IS_CLASSIC_ENEMY(element2)) &&
8471 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8472 element1 != EL_FLAMES && element2 != EL_FLAMES)
8474 ResetGfxAnimation(x, y);
8475 GfxAction[x][y] = ACTION_ATTACKING;
8477 if (IS_PLAYER(x, y))
8478 DrawPlayerField(x, y);
8480 DrawLevelField(x, y);
8482 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8484 MovDelay[x][y] = 50;
8488 RemoveField(newx, newy);
8490 Feld[newx][newy] = EL_FLAMES;
8491 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8494 RemoveField(newx1, newy1);
8496 Feld[newx1][newy1] = EL_FLAMES;
8498 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8501 RemoveField(newx2, newy2);
8503 Feld[newx2][newy2] = EL_FLAMES;
8510 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8511 Feld[newx][newy] == EL_DIAMOND)
8513 if (IS_MOVING(newx, newy))
8514 RemoveMovingField(newx, newy);
8517 Feld[newx][newy] = EL_EMPTY;
8518 DrawLevelField(newx, newy);
8521 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8523 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8524 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8526 if (AmoebaNr[newx][newy])
8528 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8529 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8530 Feld[newx][newy] == EL_BD_AMOEBA)
8531 AmoebaCnt[AmoebaNr[newx][newy]]--;
8536 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8538 RemoveMovingField(newx, newy);
8541 if (IS_MOVING(newx, newy))
8543 RemoveMovingField(newx, newy);
8548 Feld[newx][newy] = EL_EMPTY;
8549 DrawLevelField(newx, newy);
8552 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8554 else if ((element == EL_PACMAN || element == EL_MOLE)
8555 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8557 if (AmoebaNr[newx][newy])
8559 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8560 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8561 Feld[newx][newy] == EL_BD_AMOEBA)
8562 AmoebaCnt[AmoebaNr[newx][newy]]--;
8565 if (element == EL_MOLE)
8567 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8568 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8570 ResetGfxAnimation(x, y);
8571 GfxAction[x][y] = ACTION_DIGGING;
8572 DrawLevelField(x, y);
8574 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8576 return; /* wait for shrinking amoeba */
8578 else /* element == EL_PACMAN */
8580 Feld[newx][newy] = EL_EMPTY;
8581 DrawLevelField(newx, newy);
8582 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8585 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8586 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8587 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8589 /* wait for shrinking amoeba to completely disappear */
8592 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8594 /* object was running against a wall */
8599 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8600 if (move_pattern & MV_ANY_DIRECTION &&
8601 move_pattern == MovDir[x][y])
8603 int blocking_element =
8604 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8606 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8609 element = Feld[x][y]; /* element might have changed */
8613 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8614 DrawLevelElementAnimation(x, y, element);
8616 if (DONT_TOUCH(element))
8617 TestIfBadThingTouchesPlayer(x, y);
8622 InitMovingField(x, y, MovDir[x][y]);
8624 PlayLevelSoundAction(x, y, ACTION_MOVING);
8628 ContinueMoving(x, y);
8631 void ContinueMoving(int x, int y)
8633 int element = Feld[x][y];
8634 struct ElementInfo *ei = &element_info[element];
8635 int direction = MovDir[x][y];
8636 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8637 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8638 int newx = x + dx, newy = y + dy;
8639 int stored = Store[x][y];
8640 int stored_new = Store[newx][newy];
8641 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
8642 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8643 boolean last_line = (newy == lev_fieldy - 1);
8645 MovPos[x][y] += getElementMoveStepsize(x, y);
8647 if (pushed_by_player) /* special case: moving object pushed by player */
8648 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8650 if (ABS(MovPos[x][y]) < TILEX)
8653 int ee = Feld[x][y];
8654 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8655 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8657 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8658 x, y, ABS(MovPos[x][y]),
8660 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8663 DrawLevelField(x, y);
8665 return; /* element is still moving */
8668 /* element reached destination field */
8670 Feld[x][y] = EL_EMPTY;
8671 Feld[newx][newy] = element;
8672 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8674 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
8676 element = Feld[newx][newy] = EL_ACID;
8678 else if (element == EL_MOLE)
8680 Feld[x][y] = EL_SAND;
8682 DrawLevelFieldCrumbledSandNeighbours(x, y);
8684 else if (element == EL_QUICKSAND_FILLING)
8686 element = Feld[newx][newy] = get_next_element(element);
8687 Store[newx][newy] = Store[x][y];
8689 else if (element == EL_QUICKSAND_EMPTYING)
8691 Feld[x][y] = get_next_element(element);
8692 element = Feld[newx][newy] = Store[x][y];
8694 else if (element == EL_QUICKSAND_FAST_FILLING)
8696 element = Feld[newx][newy] = get_next_element(element);
8697 Store[newx][newy] = Store[x][y];
8699 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8701 Feld[x][y] = get_next_element(element);
8702 element = Feld[newx][newy] = Store[x][y];
8704 else if (element == EL_MAGIC_WALL_FILLING)
8706 element = Feld[newx][newy] = get_next_element(element);
8707 if (!game.magic_wall_active)
8708 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8709 Store[newx][newy] = Store[x][y];
8711 else if (element == EL_MAGIC_WALL_EMPTYING)
8713 Feld[x][y] = get_next_element(element);
8714 if (!game.magic_wall_active)
8715 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8716 element = Feld[newx][newy] = Store[x][y];
8718 #if USE_NEW_CUSTOM_VALUE
8719 InitField(newx, newy, FALSE);
8722 else if (element == EL_BD_MAGIC_WALL_FILLING)
8724 element = Feld[newx][newy] = get_next_element(element);
8725 if (!game.magic_wall_active)
8726 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8727 Store[newx][newy] = Store[x][y];
8729 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8731 Feld[x][y] = get_next_element(element);
8732 if (!game.magic_wall_active)
8733 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8734 element = Feld[newx][newy] = Store[x][y];
8736 #if USE_NEW_CUSTOM_VALUE
8737 InitField(newx, newy, FALSE);
8740 else if (element == EL_DC_MAGIC_WALL_FILLING)
8742 element = Feld[newx][newy] = get_next_element(element);
8743 if (!game.magic_wall_active)
8744 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8745 Store[newx][newy] = Store[x][y];
8747 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8749 Feld[x][y] = get_next_element(element);
8750 if (!game.magic_wall_active)
8751 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8752 element = Feld[newx][newy] = Store[x][y];
8754 #if USE_NEW_CUSTOM_VALUE
8755 InitField(newx, newy, FALSE);
8758 else if (element == EL_AMOEBA_DROPPING)
8760 Feld[x][y] = get_next_element(element);
8761 element = Feld[newx][newy] = Store[x][y];
8763 else if (element == EL_SOKOBAN_OBJECT)
8766 Feld[x][y] = Back[x][y];
8768 if (Back[newx][newy])
8769 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8771 Back[x][y] = Back[newx][newy] = 0;
8774 Store[x][y] = EL_EMPTY;
8779 MovDelay[newx][newy] = 0;
8781 if (CAN_CHANGE_OR_HAS_ACTION(element))
8783 /* copy element change control values to new field */
8784 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8785 ChangePage[newx][newy] = ChangePage[x][y];
8786 ChangeCount[newx][newy] = ChangeCount[x][y];
8787 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8790 #if USE_NEW_CUSTOM_VALUE
8791 CustomValue[newx][newy] = CustomValue[x][y];
8794 ChangeDelay[x][y] = 0;
8795 ChangePage[x][y] = -1;
8796 ChangeCount[x][y] = 0;
8797 ChangeEvent[x][y] = -1;
8799 #if USE_NEW_CUSTOM_VALUE
8800 CustomValue[x][y] = 0;
8803 /* copy animation control values to new field */
8804 GfxFrame[newx][newy] = GfxFrame[x][y];
8805 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
8806 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
8807 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
8809 Pushed[x][y] = Pushed[newx][newy] = FALSE;
8811 /* some elements can leave other elements behind after moving */
8813 if (ei->move_leave_element != EL_EMPTY &&
8814 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8815 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8817 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
8818 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8819 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8822 int move_leave_element = ei->move_leave_element;
8826 /* this makes it possible to leave the removed element again */
8827 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8828 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8830 /* this makes it possible to leave the removed element again */
8831 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8832 move_leave_element = stored;
8835 /* this makes it possible to leave the removed element again */
8836 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8837 ei->move_leave_element == EL_TRIGGER_ELEMENT)
8838 move_leave_element = stored;
8841 Feld[x][y] = move_leave_element;
8843 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8844 MovDir[x][y] = direction;
8846 InitField(x, y, FALSE);
8848 if (GFX_CRUMBLED(Feld[x][y]))
8849 DrawLevelFieldCrumbledSandNeighbours(x, y);
8851 if (ELEM_IS_PLAYER(move_leave_element))
8852 RelocatePlayer(x, y, move_leave_element);
8855 /* do this after checking for left-behind element */
8856 ResetGfxAnimation(x, y); /* reset animation values for old field */
8858 if (!CAN_MOVE(element) ||
8859 (CAN_FALL(element) && direction == MV_DOWN &&
8860 (element == EL_SPRING ||
8861 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8862 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8863 GfxDir[x][y] = MovDir[newx][newy] = 0;
8865 DrawLevelField(x, y);
8866 DrawLevelField(newx, newy);
8868 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8870 /* prevent pushed element from moving on in pushed direction */
8871 if (pushed_by_player && CAN_MOVE(element) &&
8872 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8873 !(element_info[element].move_pattern & direction))
8874 TurnRound(newx, newy);
8876 /* prevent elements on conveyor belt from moving on in last direction */
8877 if (pushed_by_conveyor && CAN_FALL(element) &&
8878 direction & MV_HORIZONTAL)
8879 MovDir[newx][newy] = 0;
8881 if (!pushed_by_player)
8883 int nextx = newx + dx, nexty = newy + dy;
8884 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8886 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8888 if (CAN_FALL(element) && direction == MV_DOWN)
8889 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8891 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8892 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8894 #if USE_FIX_IMPACT_COLLISION
8895 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8896 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8900 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8902 TestIfBadThingTouchesPlayer(newx, newy);
8903 TestIfBadThingTouchesFriend(newx, newy);
8905 if (!IS_CUSTOM_ELEMENT(element))
8906 TestIfBadThingTouchesOtherBadThing(newx, newy);
8908 else if (element == EL_PENGUIN)
8909 TestIfFriendTouchesBadThing(newx, newy);
8911 /* give the player one last chance (one more frame) to move away */
8912 if (CAN_FALL(element) && direction == MV_DOWN &&
8913 (last_line || (!IS_FREE(x, newy + 1) &&
8914 (!IS_PLAYER(x, newy + 1) ||
8915 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8918 if (pushed_by_player && !game.use_change_when_pushing_bug)
8920 int push_side = MV_DIR_OPPOSITE(direction);
8921 struct PlayerInfo *player = PLAYERINFO(x, y);
8923 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8924 player->index_bit, push_side);
8925 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8926 player->index_bit, push_side);
8929 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
8930 MovDelay[newx][newy] = 1;
8932 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8934 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
8937 if (ChangePage[newx][newy] != -1) /* delayed change */
8939 int page = ChangePage[newx][newy];
8940 struct ElementChangeInfo *change = &ei->change_page[page];
8942 ChangePage[newx][newy] = -1;
8944 if (change->can_change)
8946 if (ChangeElement(newx, newy, element, page))
8948 if (change->post_change_function)
8949 change->post_change_function(newx, newy);
8953 if (change->has_action)
8954 ExecuteCustomElementAction(newx, newy, element, page);
8958 TestIfElementHitsCustomElement(newx, newy, direction);
8959 TestIfPlayerTouchesCustomElement(newx, newy);
8960 TestIfElementTouchesCustomElement(newx, newy);
8962 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8963 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8964 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8965 MV_DIR_OPPOSITE(direction));
8968 int AmoebeNachbarNr(int ax, int ay)
8971 int element = Feld[ax][ay];
8973 static int xy[4][2] =
8981 for (i = 0; i < NUM_DIRECTIONS; i++)
8983 int x = ax + xy[i][0];
8984 int y = ay + xy[i][1];
8986 if (!IN_LEV_FIELD(x, y))
8989 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8990 group_nr = AmoebaNr[x][y];
8996 void AmoebenVereinigen(int ax, int ay)
8998 int i, x, y, xx, yy;
8999 int new_group_nr = AmoebaNr[ax][ay];
9000 static int xy[4][2] =
9008 if (new_group_nr == 0)
9011 for (i = 0; i < NUM_DIRECTIONS; i++)
9016 if (!IN_LEV_FIELD(x, y))
9019 if ((Feld[x][y] == EL_AMOEBA_FULL ||
9020 Feld[x][y] == EL_BD_AMOEBA ||
9021 Feld[x][y] == EL_AMOEBA_DEAD) &&
9022 AmoebaNr[x][y] != new_group_nr)
9024 int old_group_nr = AmoebaNr[x][y];
9026 if (old_group_nr == 0)
9029 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9030 AmoebaCnt[old_group_nr] = 0;
9031 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9032 AmoebaCnt2[old_group_nr] = 0;
9034 SCAN_PLAYFIELD(xx, yy)
9036 if (AmoebaNr[xx][yy] == old_group_nr)
9037 AmoebaNr[xx][yy] = new_group_nr;
9043 void AmoebeUmwandeln(int ax, int ay)
9047 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9049 int group_nr = AmoebaNr[ax][ay];
9054 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9055 printf("AmoebeUmwandeln(): This should never happen!\n");
9060 SCAN_PLAYFIELD(x, y)
9062 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9065 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9069 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9070 SND_AMOEBA_TURNING_TO_GEM :
9071 SND_AMOEBA_TURNING_TO_ROCK));
9076 static int xy[4][2] =
9084 for (i = 0; i < NUM_DIRECTIONS; i++)
9089 if (!IN_LEV_FIELD(x, y))
9092 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9094 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9095 SND_AMOEBA_TURNING_TO_GEM :
9096 SND_AMOEBA_TURNING_TO_ROCK));
9103 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9106 int group_nr = AmoebaNr[ax][ay];
9107 boolean done = FALSE;
9112 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9113 printf("AmoebeUmwandelnBD(): This should never happen!\n");
9118 SCAN_PLAYFIELD(x, y)
9120 if (AmoebaNr[x][y] == group_nr &&
9121 (Feld[x][y] == EL_AMOEBA_DEAD ||
9122 Feld[x][y] == EL_BD_AMOEBA ||
9123 Feld[x][y] == EL_AMOEBA_GROWING))
9126 Feld[x][y] = new_element;
9127 InitField(x, y, FALSE);
9128 DrawLevelField(x, y);
9134 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9135 SND_BD_AMOEBA_TURNING_TO_ROCK :
9136 SND_BD_AMOEBA_TURNING_TO_GEM));
9139 void AmoebeWaechst(int x, int y)
9141 static unsigned long sound_delay = 0;
9142 static unsigned long sound_delay_value = 0;
9144 if (!MovDelay[x][y]) /* start new growing cycle */
9148 if (DelayReached(&sound_delay, sound_delay_value))
9150 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9151 sound_delay_value = 30;
9155 if (MovDelay[x][y]) /* wait some time before growing bigger */
9158 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9160 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9161 6 - MovDelay[x][y]);
9163 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9166 if (!MovDelay[x][y])
9168 Feld[x][y] = Store[x][y];
9170 DrawLevelField(x, y);
9175 void AmoebaDisappearing(int x, int y)
9177 static unsigned long sound_delay = 0;
9178 static unsigned long sound_delay_value = 0;
9180 if (!MovDelay[x][y]) /* start new shrinking cycle */
9184 if (DelayReached(&sound_delay, sound_delay_value))
9185 sound_delay_value = 30;
9188 if (MovDelay[x][y]) /* wait some time before shrinking */
9191 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9193 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9194 6 - MovDelay[x][y]);
9196 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9199 if (!MovDelay[x][y])
9201 Feld[x][y] = EL_EMPTY;
9202 DrawLevelField(x, y);
9204 /* don't let mole enter this field in this cycle;
9205 (give priority to objects falling to this field from above) */
9211 void AmoebeAbleger(int ax, int ay)
9214 int element = Feld[ax][ay];
9215 int graphic = el2img(element);
9216 int newax = ax, neway = ay;
9217 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9218 static int xy[4][2] =
9226 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9228 Feld[ax][ay] = EL_AMOEBA_DEAD;
9229 DrawLevelField(ax, ay);
9233 if (IS_ANIMATED(graphic))
9234 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9236 if (!MovDelay[ax][ay]) /* start making new amoeba field */
9237 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9239 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
9242 if (MovDelay[ax][ay])
9246 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9249 int x = ax + xy[start][0];
9250 int y = ay + xy[start][1];
9252 if (!IN_LEV_FIELD(x, y))
9255 if (IS_FREE(x, y) ||
9256 CAN_GROW_INTO(Feld[x][y]) ||
9257 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9258 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9264 if (newax == ax && neway == ay)
9267 else /* normal or "filled" (BD style) amoeba */
9270 boolean waiting_for_player = FALSE;
9272 for (i = 0; i < NUM_DIRECTIONS; i++)
9274 int j = (start + i) % 4;
9275 int x = ax + xy[j][0];
9276 int y = ay + xy[j][1];
9278 if (!IN_LEV_FIELD(x, y))
9281 if (IS_FREE(x, y) ||
9282 CAN_GROW_INTO(Feld[x][y]) ||
9283 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9284 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9290 else if (IS_PLAYER(x, y))
9291 waiting_for_player = TRUE;
9294 if (newax == ax && neway == ay) /* amoeba cannot grow */
9296 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9298 Feld[ax][ay] = EL_AMOEBA_DEAD;
9299 DrawLevelField(ax, ay);
9300 AmoebaCnt[AmoebaNr[ax][ay]]--;
9302 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
9304 if (element == EL_AMOEBA_FULL)
9305 AmoebeUmwandeln(ax, ay);
9306 else if (element == EL_BD_AMOEBA)
9307 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9312 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9314 /* amoeba gets larger by growing in some direction */
9316 int new_group_nr = AmoebaNr[ax][ay];
9319 if (new_group_nr == 0)
9321 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9322 printf("AmoebeAbleger(): This should never happen!\n");
9327 AmoebaNr[newax][neway] = new_group_nr;
9328 AmoebaCnt[new_group_nr]++;
9329 AmoebaCnt2[new_group_nr]++;
9331 /* if amoeba touches other amoeba(s) after growing, unify them */
9332 AmoebenVereinigen(newax, neway);
9334 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9336 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9342 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9343 (neway == lev_fieldy - 1 && newax != ax))
9345 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
9346 Store[newax][neway] = element;
9348 else if (neway == ay || element == EL_EMC_DRIPPER)
9350 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
9352 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9356 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
9357 Feld[ax][ay] = EL_AMOEBA_DROPPING;
9358 Store[ax][ay] = EL_AMOEBA_DROP;
9359 ContinueMoving(ax, ay);
9363 DrawLevelField(newax, neway);
9366 void Life(int ax, int ay)
9370 int element = Feld[ax][ay];
9371 int graphic = el2img(element);
9372 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9374 boolean changed = FALSE;
9376 if (IS_ANIMATED(graphic))
9377 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9382 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
9383 MovDelay[ax][ay] = life_time;
9385 if (MovDelay[ax][ay]) /* wait some time before next cycle */
9388 if (MovDelay[ax][ay])
9392 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9394 int xx = ax+x1, yy = ay+y1;
9397 if (!IN_LEV_FIELD(xx, yy))
9400 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9402 int x = xx+x2, y = yy+y2;
9404 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9407 if (((Feld[x][y] == element ||
9408 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9410 (IS_FREE(x, y) && Stop[x][y]))
9414 if (xx == ax && yy == ay) /* field in the middle */
9416 if (nachbarn < life_parameter[0] ||
9417 nachbarn > life_parameter[1])
9419 Feld[xx][yy] = EL_EMPTY;
9421 DrawLevelField(xx, yy);
9422 Stop[xx][yy] = TRUE;
9426 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9427 { /* free border field */
9428 if (nachbarn >= life_parameter[2] &&
9429 nachbarn <= life_parameter[3])
9431 Feld[xx][yy] = element;
9432 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9434 DrawLevelField(xx, yy);
9435 Stop[xx][yy] = TRUE;
9442 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9443 SND_GAME_OF_LIFE_GROWING);
9446 static void InitRobotWheel(int x, int y)
9448 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9451 static void RunRobotWheel(int x, int y)
9453 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9456 static void StopRobotWheel(int x, int y)
9458 if (ZX == x && ZY == y)
9462 game.robot_wheel_active = FALSE;
9466 static void InitTimegateWheel(int x, int y)
9468 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9471 static void RunTimegateWheel(int x, int y)
9473 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9476 static void InitMagicBallDelay(int x, int y)
9479 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9481 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9485 static void ActivateMagicBall(int bx, int by)
9489 if (level.ball_random)
9491 int pos_border = RND(8); /* select one of the eight border elements */
9492 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9493 int xx = pos_content % 3;
9494 int yy = pos_content / 3;
9499 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9500 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9504 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9506 int xx = x - bx + 1;
9507 int yy = y - by + 1;
9509 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9510 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9514 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9517 void CheckExit(int x, int y)
9519 if (local_player->gems_still_needed > 0 ||
9520 local_player->sokobanfields_still_needed > 0 ||
9521 local_player->lights_still_needed > 0)
9523 int element = Feld[x][y];
9524 int graphic = el2img(element);
9526 if (IS_ANIMATED(graphic))
9527 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9532 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9535 Feld[x][y] = EL_EXIT_OPENING;
9537 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9540 void CheckExitEM(int x, int y)
9542 if (local_player->gems_still_needed > 0 ||
9543 local_player->sokobanfields_still_needed > 0 ||
9544 local_player->lights_still_needed > 0)
9546 int element = Feld[x][y];
9547 int graphic = el2img(element);
9549 if (IS_ANIMATED(graphic))
9550 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9555 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9558 Feld[x][y] = EL_EM_EXIT_OPENING;
9560 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9563 void CheckExitSteel(int x, int y)
9565 if (local_player->gems_still_needed > 0 ||
9566 local_player->sokobanfields_still_needed > 0 ||
9567 local_player->lights_still_needed > 0)
9569 int element = Feld[x][y];
9570 int graphic = el2img(element);
9572 if (IS_ANIMATED(graphic))
9573 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9578 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9581 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9583 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9586 void CheckExitSteelEM(int x, int y)
9588 if (local_player->gems_still_needed > 0 ||
9589 local_player->sokobanfields_still_needed > 0 ||
9590 local_player->lights_still_needed > 0)
9592 int element = Feld[x][y];
9593 int graphic = el2img(element);
9595 if (IS_ANIMATED(graphic))
9596 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9601 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9604 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9606 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9609 void CheckExitSP(int x, int y)
9611 if (local_player->gems_still_needed > 0)
9613 int element = Feld[x][y];
9614 int graphic = el2img(element);
9616 if (IS_ANIMATED(graphic))
9617 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9622 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9625 Feld[x][y] = EL_SP_EXIT_OPENING;
9627 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9630 static void CloseAllOpenTimegates()
9634 SCAN_PLAYFIELD(x, y)
9636 int element = Feld[x][y];
9638 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9640 Feld[x][y] = EL_TIMEGATE_CLOSING;
9642 PlayLevelSoundAction(x, y, ACTION_CLOSING);
9647 void DrawTwinkleOnField(int x, int y)
9649 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9652 if (Feld[x][y] == EL_BD_DIAMOND)
9655 if (MovDelay[x][y] == 0) /* next animation frame */
9656 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9658 if (MovDelay[x][y] != 0) /* wait some time before next frame */
9662 DrawLevelElementAnimation(x, y, Feld[x][y]);
9664 if (MovDelay[x][y] != 0)
9666 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9667 10 - MovDelay[x][y]);
9669 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9674 void MauerWaechst(int x, int y)
9678 if (!MovDelay[x][y]) /* next animation frame */
9679 MovDelay[x][y] = 3 * delay;
9681 if (MovDelay[x][y]) /* wait some time before next frame */
9685 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9687 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9688 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9690 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9693 if (!MovDelay[x][y])
9695 if (MovDir[x][y] == MV_LEFT)
9697 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9698 DrawLevelField(x - 1, y);
9700 else if (MovDir[x][y] == MV_RIGHT)
9702 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9703 DrawLevelField(x + 1, y);
9705 else if (MovDir[x][y] == MV_UP)
9707 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9708 DrawLevelField(x, y - 1);
9712 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9713 DrawLevelField(x, y + 1);
9716 Feld[x][y] = Store[x][y];
9718 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9719 DrawLevelField(x, y);
9724 void MauerAbleger(int ax, int ay)
9726 int element = Feld[ax][ay];
9727 int graphic = el2img(element);
9728 boolean oben_frei = FALSE, unten_frei = FALSE;
9729 boolean links_frei = FALSE, rechts_frei = FALSE;
9730 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9731 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9732 boolean new_wall = FALSE;
9734 if (IS_ANIMATED(graphic))
9735 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9737 if (!MovDelay[ax][ay]) /* start building new wall */
9738 MovDelay[ax][ay] = 6;
9740 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9743 if (MovDelay[ax][ay])
9747 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9749 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9751 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9753 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9756 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9757 element == EL_EXPANDABLE_WALL_ANY)
9761 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9762 Store[ax][ay-1] = element;
9763 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9764 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9765 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9766 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9771 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9772 Store[ax][ay+1] = element;
9773 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9774 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9775 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9776 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9781 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9782 element == EL_EXPANDABLE_WALL_ANY ||
9783 element == EL_EXPANDABLE_WALL ||
9784 element == EL_BD_EXPANDABLE_WALL)
9788 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9789 Store[ax-1][ay] = element;
9790 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9791 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9792 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9793 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9799 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9800 Store[ax+1][ay] = element;
9801 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9802 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9803 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9804 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9809 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9810 DrawLevelField(ax, ay);
9812 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9814 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9815 unten_massiv = TRUE;
9816 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9817 links_massiv = TRUE;
9818 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9819 rechts_massiv = TRUE;
9821 if (((oben_massiv && unten_massiv) ||
9822 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9823 element == EL_EXPANDABLE_WALL) &&
9824 ((links_massiv && rechts_massiv) ||
9825 element == EL_EXPANDABLE_WALL_VERTICAL))
9826 Feld[ax][ay] = EL_WALL;
9829 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9832 void MauerAblegerStahl(int ax, int ay)
9834 int element = Feld[ax][ay];
9835 int graphic = el2img(element);
9836 boolean oben_frei = FALSE, unten_frei = FALSE;
9837 boolean links_frei = FALSE, rechts_frei = FALSE;
9838 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9839 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9840 boolean new_wall = FALSE;
9842 if (IS_ANIMATED(graphic))
9843 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9845 if (!MovDelay[ax][ay]) /* start building new wall */
9846 MovDelay[ax][ay] = 6;
9848 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9851 if (MovDelay[ax][ay])
9855 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9857 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9859 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9861 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9864 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9865 element == EL_EXPANDABLE_STEELWALL_ANY)
9869 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9870 Store[ax][ay-1] = element;
9871 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9872 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9873 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9874 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9879 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9880 Store[ax][ay+1] = element;
9881 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9882 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9883 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9884 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9889 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9890 element == EL_EXPANDABLE_STEELWALL_ANY)
9894 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9895 Store[ax-1][ay] = element;
9896 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9897 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9898 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9899 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9905 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9906 Store[ax+1][ay] = element;
9907 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9908 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9909 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9910 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9915 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9917 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9918 unten_massiv = TRUE;
9919 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9920 links_massiv = TRUE;
9921 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9922 rechts_massiv = TRUE;
9924 if (((oben_massiv && unten_massiv) ||
9925 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9926 ((links_massiv && rechts_massiv) ||
9927 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9928 Feld[ax][ay] = EL_STEELWALL;
9931 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9934 void CheckForDragon(int x, int y)
9937 boolean dragon_found = FALSE;
9938 static int xy[4][2] =
9946 for (i = 0; i < NUM_DIRECTIONS; i++)
9948 for (j = 0; j < 4; j++)
9950 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9952 if (IN_LEV_FIELD(xx, yy) &&
9953 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9955 if (Feld[xx][yy] == EL_DRAGON)
9956 dragon_found = TRUE;
9965 for (i = 0; i < NUM_DIRECTIONS; i++)
9967 for (j = 0; j < 3; j++)
9969 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9971 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9973 Feld[xx][yy] = EL_EMPTY;
9974 DrawLevelField(xx, yy);
9983 static void InitBuggyBase(int x, int y)
9985 int element = Feld[x][y];
9986 int activating_delay = FRAMES_PER_SECOND / 4;
9989 (element == EL_SP_BUGGY_BASE ?
9990 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9991 element == EL_SP_BUGGY_BASE_ACTIVATING ?
9993 element == EL_SP_BUGGY_BASE_ACTIVE ?
9994 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9997 static void WarnBuggyBase(int x, int y)
10000 static int xy[4][2] =
10008 for (i = 0; i < NUM_DIRECTIONS; i++)
10010 int xx = x + xy[i][0];
10011 int yy = y + xy[i][1];
10013 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10015 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10022 static void InitTrap(int x, int y)
10024 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10027 static void ActivateTrap(int x, int y)
10029 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10032 static void ChangeActiveTrap(int x, int y)
10034 int graphic = IMG_TRAP_ACTIVE;
10036 /* if new animation frame was drawn, correct crumbled sand border */
10037 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10038 DrawLevelFieldCrumbledSand(x, y);
10041 static int getSpecialActionElement(int element, int number, int base_element)
10043 return (element != EL_EMPTY ? element :
10044 number != -1 ? base_element + number - 1 :
10048 static int getModifiedActionNumber(int value_old, int operator, int operand,
10049 int value_min, int value_max)
10051 int value_new = (operator == CA_MODE_SET ? operand :
10052 operator == CA_MODE_ADD ? value_old + operand :
10053 operator == CA_MODE_SUBTRACT ? value_old - operand :
10054 operator == CA_MODE_MULTIPLY ? value_old * operand :
10055 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
10056 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
10059 return (value_new < value_min ? value_min :
10060 value_new > value_max ? value_max :
10064 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10066 struct ElementInfo *ei = &element_info[element];
10067 struct ElementChangeInfo *change = &ei->change_page[page];
10068 int target_element = change->target_element;
10069 int action_type = change->action_type;
10070 int action_mode = change->action_mode;
10071 int action_arg = change->action_arg;
10074 if (!change->has_action)
10077 /* ---------- determine action paramater values -------------------------- */
10079 int level_time_value =
10080 (level.time > 0 ? TimeLeft :
10083 int action_arg_element =
10084 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
10085 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10086 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
10089 int action_arg_direction =
10090 (action_arg >= CA_ARG_DIRECTION_LEFT &&
10091 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10092 action_arg == CA_ARG_DIRECTION_TRIGGER ?
10093 change->actual_trigger_side :
10094 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10095 MV_DIR_OPPOSITE(change->actual_trigger_side) :
10098 int action_arg_number_min =
10099 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10102 int action_arg_number_max =
10103 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10104 action_type == CA_SET_LEVEL_GEMS ? 999 :
10105 action_type == CA_SET_LEVEL_TIME ? 9999 :
10106 action_type == CA_SET_LEVEL_SCORE ? 99999 :
10107 action_type == CA_SET_CE_VALUE ? 9999 :
10108 action_type == CA_SET_CE_SCORE ? 9999 :
10111 int action_arg_number_reset =
10112 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10113 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10114 action_type == CA_SET_LEVEL_TIME ? level.time :
10115 action_type == CA_SET_LEVEL_SCORE ? 0 :
10116 #if USE_NEW_CUSTOM_VALUE
10117 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10119 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10121 action_type == CA_SET_CE_SCORE ? 0 :
10124 int action_arg_number =
10125 (action_arg <= CA_ARG_MAX ? action_arg :
10126 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10127 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10128 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10129 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10130 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10131 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10132 #if USE_NEW_CUSTOM_VALUE
10133 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10135 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10137 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10138 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10139 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10140 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10141 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10142 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10143 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10144 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10145 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10146 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
10147 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10150 int action_arg_number_old =
10151 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10152 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10153 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10154 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10155 action_type == CA_SET_CE_SCORE ? ei->collect_score :
10158 int action_arg_number_new =
10159 getModifiedActionNumber(action_arg_number_old,
10160 action_mode, action_arg_number,
10161 action_arg_number_min, action_arg_number_max);
10164 int trigger_player_bits = change->actual_trigger_player_bits;
10166 int trigger_player_bits =
10167 (change->actual_trigger_player >= EL_PLAYER_1 &&
10168 change->actual_trigger_player <= EL_PLAYER_4 ?
10169 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10173 int action_arg_player_bits =
10174 (action_arg >= CA_ARG_PLAYER_1 &&
10175 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10176 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10179 /* ---------- execute action -------------------------------------------- */
10181 switch (action_type)
10188 /* ---------- level actions ------------------------------------------- */
10190 case CA_RESTART_LEVEL:
10192 game.restart_level = TRUE;
10197 case CA_SHOW_ENVELOPE:
10199 int element = getSpecialActionElement(action_arg_element,
10200 action_arg_number, EL_ENVELOPE_1);
10202 if (IS_ENVELOPE(element))
10203 local_player->show_envelope = element;
10208 case CA_SET_LEVEL_TIME:
10210 if (level.time > 0) /* only modify limited time value */
10212 TimeLeft = action_arg_number_new;
10215 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10217 DisplayGameControlValues();
10219 DrawGameValue_Time(TimeLeft);
10222 if (!TimeLeft && setup.time_limit)
10223 for (i = 0; i < MAX_PLAYERS; i++)
10224 KillPlayer(&stored_player[i]);
10230 case CA_SET_LEVEL_SCORE:
10232 local_player->score = action_arg_number_new;
10235 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10237 DisplayGameControlValues();
10239 DrawGameValue_Score(local_player->score);
10245 case CA_SET_LEVEL_GEMS:
10247 local_player->gems_still_needed = action_arg_number_new;
10250 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10252 DisplayGameControlValues();
10254 DrawGameValue_Emeralds(local_player->gems_still_needed);
10260 #if !USE_PLAYER_GRAVITY
10261 case CA_SET_LEVEL_GRAVITY:
10263 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10264 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10265 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10271 case CA_SET_LEVEL_WIND:
10273 game.wind_direction = action_arg_direction;
10278 /* ---------- player actions ------------------------------------------ */
10280 case CA_MOVE_PLAYER:
10282 /* automatically move to the next field in specified direction */
10283 for (i = 0; i < MAX_PLAYERS; i++)
10284 if (trigger_player_bits & (1 << i))
10285 stored_player[i].programmed_action = action_arg_direction;
10290 case CA_EXIT_PLAYER:
10292 for (i = 0; i < MAX_PLAYERS; i++)
10293 if (action_arg_player_bits & (1 << i))
10294 PlayerWins(&stored_player[i]);
10299 case CA_KILL_PLAYER:
10301 for (i = 0; i < MAX_PLAYERS; i++)
10302 if (action_arg_player_bits & (1 << i))
10303 KillPlayer(&stored_player[i]);
10308 case CA_SET_PLAYER_KEYS:
10310 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10311 int element = getSpecialActionElement(action_arg_element,
10312 action_arg_number, EL_KEY_1);
10314 if (IS_KEY(element))
10316 for (i = 0; i < MAX_PLAYERS; i++)
10318 if (trigger_player_bits & (1 << i))
10320 stored_player[i].key[KEY_NR(element)] = key_state;
10322 DrawGameDoorValues();
10330 case CA_SET_PLAYER_SPEED:
10332 for (i = 0; i < MAX_PLAYERS; i++)
10334 if (trigger_player_bits & (1 << i))
10336 int move_stepsize = TILEX / stored_player[i].move_delay_value;
10338 if (action_arg == CA_ARG_SPEED_FASTER &&
10339 stored_player[i].cannot_move)
10341 action_arg_number = STEPSIZE_VERY_SLOW;
10343 else if (action_arg == CA_ARG_SPEED_SLOWER ||
10344 action_arg == CA_ARG_SPEED_FASTER)
10346 action_arg_number = 2;
10347 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10350 else if (action_arg == CA_ARG_NUMBER_RESET)
10352 action_arg_number = level.initial_player_stepsize[i];
10356 getModifiedActionNumber(move_stepsize,
10359 action_arg_number_min,
10360 action_arg_number_max);
10362 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10369 case CA_SET_PLAYER_SHIELD:
10371 for (i = 0; i < MAX_PLAYERS; i++)
10373 if (trigger_player_bits & (1 << i))
10375 if (action_arg == CA_ARG_SHIELD_OFF)
10377 stored_player[i].shield_normal_time_left = 0;
10378 stored_player[i].shield_deadly_time_left = 0;
10380 else if (action_arg == CA_ARG_SHIELD_NORMAL)
10382 stored_player[i].shield_normal_time_left = 999999;
10384 else if (action_arg == CA_ARG_SHIELD_DEADLY)
10386 stored_player[i].shield_normal_time_left = 999999;
10387 stored_player[i].shield_deadly_time_left = 999999;
10395 #if USE_PLAYER_GRAVITY
10396 case CA_SET_PLAYER_GRAVITY:
10398 for (i = 0; i < MAX_PLAYERS; i++)
10400 if (trigger_player_bits & (1 << i))
10402 stored_player[i].gravity =
10403 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10404 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10405 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10406 stored_player[i].gravity);
10414 case CA_SET_PLAYER_ARTWORK:
10416 for (i = 0; i < MAX_PLAYERS; i++)
10418 if (trigger_player_bits & (1 << i))
10420 int artwork_element = action_arg_element;
10422 if (action_arg == CA_ARG_ELEMENT_RESET)
10424 (level.use_artwork_element[i] ? level.artwork_element[i] :
10425 stored_player[i].element_nr);
10427 #if USE_GFX_RESET_PLAYER_ARTWORK
10428 if (stored_player[i].artwork_element != artwork_element)
10429 stored_player[i].Frame = 0;
10432 stored_player[i].artwork_element = artwork_element;
10434 SetPlayerWaiting(&stored_player[i], FALSE);
10436 /* set number of special actions for bored and sleeping animation */
10437 stored_player[i].num_special_action_bored =
10438 get_num_special_action(artwork_element,
10439 ACTION_BORING_1, ACTION_BORING_LAST);
10440 stored_player[i].num_special_action_sleeping =
10441 get_num_special_action(artwork_element,
10442 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10449 /* ---------- CE actions ---------------------------------------------- */
10451 case CA_SET_CE_VALUE:
10453 #if USE_NEW_CUSTOM_VALUE
10454 int last_ce_value = CustomValue[x][y];
10456 CustomValue[x][y] = action_arg_number_new;
10458 if (CustomValue[x][y] != last_ce_value)
10460 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10461 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10463 if (CustomValue[x][y] == 0)
10465 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10466 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10474 case CA_SET_CE_SCORE:
10476 #if USE_NEW_CUSTOM_VALUE
10477 int last_ce_score = ei->collect_score;
10479 ei->collect_score = action_arg_number_new;
10481 if (ei->collect_score != last_ce_score)
10483 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10484 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10486 if (ei->collect_score == 0)
10490 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10491 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10494 This is a very special case that seems to be a mixture between
10495 CheckElementChange() and CheckTriggeredElementChange(): while
10496 the first one only affects single elements that are triggered
10497 directly, the second one affects multiple elements in the playfield
10498 that are triggered indirectly by another element. This is a third
10499 case: Changing the CE score always affects multiple identical CEs,
10500 so every affected CE must be checked, not only the single CE for
10501 which the CE score was changed in the first place (as every instance
10502 of that CE shares the same CE score, and therefore also can change)!
10504 SCAN_PLAYFIELD(xx, yy)
10506 if (Feld[xx][yy] == element)
10507 CheckElementChange(xx, yy, element, EL_UNDEFINED,
10508 CE_SCORE_GETS_ZERO);
10517 /* ---------- engine actions ------------------------------------------ */
10519 case CA_SET_ENGINE_SCAN_MODE:
10521 InitPlayfieldScanMode(action_arg);
10531 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10533 int old_element = Feld[x][y];
10534 int new_element = GetElementFromGroupElement(element);
10535 int previous_move_direction = MovDir[x][y];
10536 #if USE_NEW_CUSTOM_VALUE
10537 int last_ce_value = CustomValue[x][y];
10539 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10540 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10541 boolean add_player_onto_element = (new_element_is_player &&
10542 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
10543 /* this breaks SnakeBite when a snake is
10544 halfway through a door that closes */
10545 /* NOW FIXED AT LEVEL INIT IN files.c */
10546 new_element != EL_SOKOBAN_FIELD_PLAYER &&
10548 IS_WALKABLE(old_element));
10551 /* check if element under the player changes from accessible to unaccessible
10552 (needed for special case of dropping element which then changes) */
10553 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10554 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10562 if (!add_player_onto_element)
10564 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10565 RemoveMovingField(x, y);
10569 Feld[x][y] = new_element;
10571 #if !USE_GFX_RESET_GFX_ANIMATION
10572 ResetGfxAnimation(x, y);
10573 ResetRandomAnimationValue(x, y);
10576 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10577 MovDir[x][y] = previous_move_direction;
10579 #if USE_NEW_CUSTOM_VALUE
10580 if (element_info[new_element].use_last_ce_value)
10581 CustomValue[x][y] = last_ce_value;
10584 InitField_WithBug1(x, y, FALSE);
10586 new_element = Feld[x][y]; /* element may have changed */
10588 #if USE_GFX_RESET_GFX_ANIMATION
10589 ResetGfxAnimation(x, y);
10590 ResetRandomAnimationValue(x, y);
10593 DrawLevelField(x, y);
10595 if (GFX_CRUMBLED(new_element))
10596 DrawLevelFieldCrumbledSandNeighbours(x, y);
10600 /* check if element under the player changes from accessible to unaccessible
10601 (needed for special case of dropping element which then changes) */
10602 /* (must be checked after creating new element for walkable group elements) */
10603 #if USE_FIX_KILLED_BY_NON_WALKABLE
10604 if (IS_PLAYER(x, y) && !player_explosion_protected &&
10605 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10612 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10613 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10622 /* "ChangeCount" not set yet to allow "entered by player" change one time */
10623 if (new_element_is_player)
10624 RelocatePlayer(x, y, new_element);
10627 ChangeCount[x][y]++; /* count number of changes in the same frame */
10629 TestIfBadThingTouchesPlayer(x, y);
10630 TestIfPlayerTouchesCustomElement(x, y);
10631 TestIfElementTouchesCustomElement(x, y);
10634 static void CreateField(int x, int y, int element)
10636 CreateFieldExt(x, y, element, FALSE);
10639 static void CreateElementFromChange(int x, int y, int element)
10641 element = GET_VALID_RUNTIME_ELEMENT(element);
10643 #if USE_STOP_CHANGED_ELEMENTS
10644 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10646 int old_element = Feld[x][y];
10648 /* prevent changed element from moving in same engine frame
10649 unless both old and new element can either fall or move */
10650 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10651 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10656 CreateFieldExt(x, y, element, TRUE);
10659 static boolean ChangeElement(int x, int y, int element, int page)
10661 struct ElementInfo *ei = &element_info[element];
10662 struct ElementChangeInfo *change = &ei->change_page[page];
10663 int ce_value = CustomValue[x][y];
10664 int ce_score = ei->collect_score;
10665 int target_element;
10666 int old_element = Feld[x][y];
10668 /* always use default change event to prevent running into a loop */
10669 if (ChangeEvent[x][y] == -1)
10670 ChangeEvent[x][y] = CE_DELAY;
10672 if (ChangeEvent[x][y] == CE_DELAY)
10674 /* reset actual trigger element, trigger player and action element */
10675 change->actual_trigger_element = EL_EMPTY;
10676 change->actual_trigger_player = EL_PLAYER_1;
10677 change->actual_trigger_player_bits = CH_PLAYER_1;
10678 change->actual_trigger_side = CH_SIDE_NONE;
10679 change->actual_trigger_ce_value = 0;
10680 change->actual_trigger_ce_score = 0;
10683 /* do not change elements more than a specified maximum number of changes */
10684 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10687 ChangeCount[x][y]++; /* count number of changes in the same frame */
10689 if (change->explode)
10696 if (change->use_target_content)
10698 boolean complete_replace = TRUE;
10699 boolean can_replace[3][3];
10702 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10705 boolean is_walkable;
10706 boolean is_diggable;
10707 boolean is_collectible;
10708 boolean is_removable;
10709 boolean is_destructible;
10710 int ex = x + xx - 1;
10711 int ey = y + yy - 1;
10712 int content_element = change->target_content.e[xx][yy];
10715 can_replace[xx][yy] = TRUE;
10717 if (ex == x && ey == y) /* do not check changing element itself */
10720 if (content_element == EL_EMPTY_SPACE)
10722 can_replace[xx][yy] = FALSE; /* do not replace border with space */
10727 if (!IN_LEV_FIELD(ex, ey))
10729 can_replace[xx][yy] = FALSE;
10730 complete_replace = FALSE;
10737 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10738 e = MovingOrBlocked2Element(ex, ey);
10740 is_empty = (IS_FREE(ex, ey) ||
10741 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10743 is_walkable = (is_empty || IS_WALKABLE(e));
10744 is_diggable = (is_empty || IS_DIGGABLE(e));
10745 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10746 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10747 is_removable = (is_diggable || is_collectible);
10749 can_replace[xx][yy] =
10750 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10751 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10752 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10753 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10754 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10755 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10756 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10758 if (!can_replace[xx][yy])
10759 complete_replace = FALSE;
10762 if (!change->only_if_complete || complete_replace)
10764 boolean something_has_changed = FALSE;
10766 if (change->only_if_complete && change->use_random_replace &&
10767 RND(100) < change->random_percentage)
10770 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10772 int ex = x + xx - 1;
10773 int ey = y + yy - 1;
10774 int content_element;
10776 if (can_replace[xx][yy] && (!change->use_random_replace ||
10777 RND(100) < change->random_percentage))
10779 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10780 RemoveMovingField(ex, ey);
10782 ChangeEvent[ex][ey] = ChangeEvent[x][y];
10784 content_element = change->target_content.e[xx][yy];
10785 target_element = GET_TARGET_ELEMENT(element, content_element, change,
10786 ce_value, ce_score);
10788 CreateElementFromChange(ex, ey, target_element);
10790 something_has_changed = TRUE;
10792 /* for symmetry reasons, freeze newly created border elements */
10793 if (ex != x || ey != y)
10794 Stop[ex][ey] = TRUE; /* no more moving in this frame */
10798 if (something_has_changed)
10800 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10801 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10807 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10808 ce_value, ce_score);
10810 if (element == EL_DIAGONAL_GROWING ||
10811 element == EL_DIAGONAL_SHRINKING)
10813 target_element = Store[x][y];
10815 Store[x][y] = EL_EMPTY;
10818 CreateElementFromChange(x, y, target_element);
10820 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10821 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10824 /* this uses direct change before indirect change */
10825 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10830 #if USE_NEW_DELAYED_ACTION
10832 static void HandleElementChange(int x, int y, int page)
10834 int element = MovingOrBlocked2Element(x, y);
10835 struct ElementInfo *ei = &element_info[element];
10836 struct ElementChangeInfo *change = &ei->change_page[page];
10839 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10840 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10843 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10844 x, y, element, element_info[element].token_name);
10845 printf("HandleElementChange(): This should never happen!\n");
10850 /* this can happen with classic bombs on walkable, changing elements */
10851 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10854 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
10855 ChangeDelay[x][y] = 0;
10861 if (ChangeDelay[x][y] == 0) /* initialize element change */
10863 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10865 if (change->can_change)
10868 /* !!! not clear why graphic animation should be reset at all here !!! */
10869 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10870 #if USE_GFX_RESET_WHEN_NOT_MOVING
10871 /* when a custom element is about to change (for example by change delay),
10872 do not reset graphic animation when the custom element is moving */
10873 if (!IS_MOVING(x, y))
10876 ResetGfxAnimation(x, y);
10877 ResetRandomAnimationValue(x, y);
10881 if (change->pre_change_function)
10882 change->pre_change_function(x, y);
10886 ChangeDelay[x][y]--;
10888 if (ChangeDelay[x][y] != 0) /* continue element change */
10890 if (change->can_change)
10892 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10894 if (IS_ANIMATED(graphic))
10895 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10897 if (change->change_function)
10898 change->change_function(x, y);
10901 else /* finish element change */
10903 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10905 page = ChangePage[x][y];
10906 ChangePage[x][y] = -1;
10908 change = &ei->change_page[page];
10911 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10913 ChangeDelay[x][y] = 1; /* try change after next move step */
10914 ChangePage[x][y] = page; /* remember page to use for change */
10919 if (change->can_change)
10921 if (ChangeElement(x, y, element, page))
10923 if (change->post_change_function)
10924 change->post_change_function(x, y);
10928 if (change->has_action)
10929 ExecuteCustomElementAction(x, y, element, page);
10935 static void HandleElementChange(int x, int y, int page)
10937 int element = MovingOrBlocked2Element(x, y);
10938 struct ElementInfo *ei = &element_info[element];
10939 struct ElementChangeInfo *change = &ei->change_page[page];
10942 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
10945 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10946 x, y, element, element_info[element].token_name);
10947 printf("HandleElementChange(): This should never happen!\n");
10952 /* this can happen with classic bombs on walkable, changing elements */
10953 if (!CAN_CHANGE(element))
10956 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
10957 ChangeDelay[x][y] = 0;
10963 if (ChangeDelay[x][y] == 0) /* initialize element change */
10965 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10967 ResetGfxAnimation(x, y);
10968 ResetRandomAnimationValue(x, y);
10970 if (change->pre_change_function)
10971 change->pre_change_function(x, y);
10974 ChangeDelay[x][y]--;
10976 if (ChangeDelay[x][y] != 0) /* continue element change */
10978 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10980 if (IS_ANIMATED(graphic))
10981 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10983 if (change->change_function)
10984 change->change_function(x, y);
10986 else /* finish element change */
10988 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10990 page = ChangePage[x][y];
10991 ChangePage[x][y] = -1;
10993 change = &ei->change_page[page];
10996 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10998 ChangeDelay[x][y] = 1; /* try change after next move step */
10999 ChangePage[x][y] = page; /* remember page to use for change */
11004 if (ChangeElement(x, y, element, page))
11006 if (change->post_change_function)
11007 change->post_change_function(x, y);
11014 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11015 int trigger_element,
11017 int trigger_player,
11021 boolean change_done_any = FALSE;
11022 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11025 if (!(trigger_events[trigger_element][trigger_event]))
11029 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11030 trigger_event, recursion_loop_depth, recursion_loop_detected,
11031 recursion_loop_element, EL_NAME(recursion_loop_element));
11034 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11036 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11038 int element = EL_CUSTOM_START + i;
11039 boolean change_done = FALSE;
11042 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11043 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11046 for (p = 0; p < element_info[element].num_change_pages; p++)
11048 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11050 if (change->can_change_or_has_action &&
11051 change->has_event[trigger_event] &&
11052 change->trigger_side & trigger_side &&
11053 change->trigger_player & trigger_player &&
11054 change->trigger_page & trigger_page_bits &&
11055 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11057 change->actual_trigger_element = trigger_element;
11058 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11059 change->actual_trigger_player_bits = trigger_player;
11060 change->actual_trigger_side = trigger_side;
11061 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11062 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11064 if ((change->can_change && !change_done) || change->has_action)
11068 SCAN_PLAYFIELD(x, y)
11070 if (Feld[x][y] == element)
11072 if (change->can_change && !change_done)
11074 ChangeDelay[x][y] = 1;
11075 ChangeEvent[x][y] = trigger_event;
11077 HandleElementChange(x, y, p);
11079 #if USE_NEW_DELAYED_ACTION
11080 else if (change->has_action)
11082 ExecuteCustomElementAction(x, y, element, p);
11083 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11086 if (change->has_action)
11088 ExecuteCustomElementAction(x, y, element, p);
11089 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11095 if (change->can_change)
11097 change_done = TRUE;
11098 change_done_any = TRUE;
11105 RECURSION_LOOP_DETECTION_END();
11107 return change_done_any;
11110 static boolean CheckElementChangeExt(int x, int y,
11112 int trigger_element,
11114 int trigger_player,
11117 boolean change_done = FALSE;
11120 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11121 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11124 if (Feld[x][y] == EL_BLOCKED)
11126 Blocked2Moving(x, y, &x, &y);
11127 element = Feld[x][y];
11131 /* check if element has already changed */
11132 if (Feld[x][y] != element)
11135 /* check if element has already changed or is about to change after moving */
11136 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11137 Feld[x][y] != element) ||
11139 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11140 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11141 ChangePage[x][y] != -1)))
11146 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11147 trigger_event, recursion_loop_depth, recursion_loop_detected,
11148 recursion_loop_element, EL_NAME(recursion_loop_element));
11151 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11153 for (p = 0; p < element_info[element].num_change_pages; p++)
11155 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11157 /* check trigger element for all events where the element that is checked
11158 for changing interacts with a directly adjacent element -- this is
11159 different to element changes that affect other elements to change on the
11160 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11161 boolean check_trigger_element =
11162 (trigger_event == CE_TOUCHING_X ||
11163 trigger_event == CE_HITTING_X ||
11164 trigger_event == CE_HIT_BY_X ||
11166 /* this one was forgotten until 3.2.3 */
11167 trigger_event == CE_DIGGING_X);
11170 if (change->can_change_or_has_action &&
11171 change->has_event[trigger_event] &&
11172 change->trigger_side & trigger_side &&
11173 change->trigger_player & trigger_player &&
11174 (!check_trigger_element ||
11175 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11177 change->actual_trigger_element = trigger_element;
11178 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11179 change->actual_trigger_player_bits = trigger_player;
11180 change->actual_trigger_side = trigger_side;
11181 change->actual_trigger_ce_value = CustomValue[x][y];
11182 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11184 /* special case: trigger element not at (x,y) position for some events */
11185 if (check_trigger_element)
11197 { 0, 0 }, { 0, 0 }, { 0, 0 },
11201 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11202 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11204 change->actual_trigger_ce_value = CustomValue[xx][yy];
11205 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11208 if (change->can_change && !change_done)
11210 ChangeDelay[x][y] = 1;
11211 ChangeEvent[x][y] = trigger_event;
11213 HandleElementChange(x, y, p);
11215 change_done = TRUE;
11217 #if USE_NEW_DELAYED_ACTION
11218 else if (change->has_action)
11220 ExecuteCustomElementAction(x, y, element, p);
11221 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11224 if (change->has_action)
11226 ExecuteCustomElementAction(x, y, element, p);
11227 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11233 RECURSION_LOOP_DETECTION_END();
11235 return change_done;
11238 static void PlayPlayerSound(struct PlayerInfo *player)
11240 int jx = player->jx, jy = player->jy;
11241 int sound_element = player->artwork_element;
11242 int last_action = player->last_action_waiting;
11243 int action = player->action_waiting;
11245 if (player->is_waiting)
11247 if (action != last_action)
11248 PlayLevelSoundElementAction(jx, jy, sound_element, action);
11250 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11254 if (action != last_action)
11255 StopSound(element_info[sound_element].sound[last_action]);
11257 if (last_action == ACTION_SLEEPING)
11258 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11262 static void PlayAllPlayersSound()
11266 for (i = 0; i < MAX_PLAYERS; i++)
11267 if (stored_player[i].active)
11268 PlayPlayerSound(&stored_player[i]);
11271 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11273 boolean last_waiting = player->is_waiting;
11274 int move_dir = player->MovDir;
11276 player->dir_waiting = move_dir;
11277 player->last_action_waiting = player->action_waiting;
11281 if (!last_waiting) /* not waiting -> waiting */
11283 player->is_waiting = TRUE;
11285 player->frame_counter_bored =
11287 game.player_boring_delay_fixed +
11288 GetSimpleRandom(game.player_boring_delay_random);
11289 player->frame_counter_sleeping =
11291 game.player_sleeping_delay_fixed +
11292 GetSimpleRandom(game.player_sleeping_delay_random);
11294 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11297 if (game.player_sleeping_delay_fixed +
11298 game.player_sleeping_delay_random > 0 &&
11299 player->anim_delay_counter == 0 &&
11300 player->post_delay_counter == 0 &&
11301 FrameCounter >= player->frame_counter_sleeping)
11302 player->is_sleeping = TRUE;
11303 else if (game.player_boring_delay_fixed +
11304 game.player_boring_delay_random > 0 &&
11305 FrameCounter >= player->frame_counter_bored)
11306 player->is_bored = TRUE;
11308 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11309 player->is_bored ? ACTION_BORING :
11312 if (player->is_sleeping && player->use_murphy)
11314 /* special case for sleeping Murphy when leaning against non-free tile */
11316 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11317 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11318 !IS_MOVING(player->jx - 1, player->jy)))
11319 move_dir = MV_LEFT;
11320 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11321 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11322 !IS_MOVING(player->jx + 1, player->jy)))
11323 move_dir = MV_RIGHT;
11325 player->is_sleeping = FALSE;
11327 player->dir_waiting = move_dir;
11330 if (player->is_sleeping)
11332 if (player->num_special_action_sleeping > 0)
11334 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11336 int last_special_action = player->special_action_sleeping;
11337 int num_special_action = player->num_special_action_sleeping;
11338 int special_action =
11339 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11340 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11341 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11342 last_special_action + 1 : ACTION_SLEEPING);
11343 int special_graphic =
11344 el_act_dir2img(player->artwork_element, special_action, move_dir);
11346 player->anim_delay_counter =
11347 graphic_info[special_graphic].anim_delay_fixed +
11348 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11349 player->post_delay_counter =
11350 graphic_info[special_graphic].post_delay_fixed +
11351 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11353 player->special_action_sleeping = special_action;
11356 if (player->anim_delay_counter > 0)
11358 player->action_waiting = player->special_action_sleeping;
11359 player->anim_delay_counter--;
11361 else if (player->post_delay_counter > 0)
11363 player->post_delay_counter--;
11367 else if (player->is_bored)
11369 if (player->num_special_action_bored > 0)
11371 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11373 int special_action =
11374 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11375 int special_graphic =
11376 el_act_dir2img(player->artwork_element, special_action, move_dir);
11378 player->anim_delay_counter =
11379 graphic_info[special_graphic].anim_delay_fixed +
11380 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11381 player->post_delay_counter =
11382 graphic_info[special_graphic].post_delay_fixed +
11383 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11385 player->special_action_bored = special_action;
11388 if (player->anim_delay_counter > 0)
11390 player->action_waiting = player->special_action_bored;
11391 player->anim_delay_counter--;
11393 else if (player->post_delay_counter > 0)
11395 player->post_delay_counter--;
11400 else if (last_waiting) /* waiting -> not waiting */
11402 player->is_waiting = FALSE;
11403 player->is_bored = FALSE;
11404 player->is_sleeping = FALSE;
11406 player->frame_counter_bored = -1;
11407 player->frame_counter_sleeping = -1;
11409 player->anim_delay_counter = 0;
11410 player->post_delay_counter = 0;
11412 player->dir_waiting = player->MovDir;
11413 player->action_waiting = ACTION_DEFAULT;
11415 player->special_action_bored = ACTION_DEFAULT;
11416 player->special_action_sleeping = ACTION_DEFAULT;
11420 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11422 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11423 int left = player_action & JOY_LEFT;
11424 int right = player_action & JOY_RIGHT;
11425 int up = player_action & JOY_UP;
11426 int down = player_action & JOY_DOWN;
11427 int button1 = player_action & JOY_BUTTON_1;
11428 int button2 = player_action & JOY_BUTTON_2;
11429 int dx = (left ? -1 : right ? 1 : 0);
11430 int dy = (up ? -1 : down ? 1 : 0);
11432 if (!player->active || tape.pausing)
11438 snapped = SnapField(player, dx, dy);
11442 dropped = DropElement(player);
11444 moved = MovePlayer(player, dx, dy);
11447 if (tape.single_step && tape.recording && !tape.pausing)
11449 if (button1 || (dropped && !moved))
11451 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11452 SnapField(player, 0, 0); /* stop snapping */
11456 SetPlayerWaiting(player, FALSE);
11458 return player_action;
11462 /* no actions for this player (no input at player's configured device) */
11464 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11465 SnapField(player, 0, 0);
11466 CheckGravityMovementWhenNotMoving(player);
11468 if (player->MovPos == 0)
11469 SetPlayerWaiting(player, TRUE);
11471 if (player->MovPos == 0) /* needed for tape.playing */
11472 player->is_moving = FALSE;
11474 player->is_dropping = FALSE;
11475 player->is_dropping_pressed = FALSE;
11476 player->drop_pressed_delay = 0;
11482 static void CheckLevelTime()
11486 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11488 if (level.native_em_level->lev->home == 0) /* all players at home */
11490 PlayerWins(local_player);
11492 AllPlayersGone = TRUE;
11494 level.native_em_level->lev->home = -1;
11497 if (level.native_em_level->ply[0]->alive == 0 &&
11498 level.native_em_level->ply[1]->alive == 0 &&
11499 level.native_em_level->ply[2]->alive == 0 &&
11500 level.native_em_level->ply[3]->alive == 0) /* all dead */
11501 AllPlayersGone = TRUE;
11504 if (TimeFrames >= FRAMES_PER_SECOND)
11509 for (i = 0; i < MAX_PLAYERS; i++)
11511 struct PlayerInfo *player = &stored_player[i];
11513 if (SHIELD_ON(player))
11515 player->shield_normal_time_left--;
11517 if (player->shield_deadly_time_left > 0)
11518 player->shield_deadly_time_left--;
11522 if (!local_player->LevelSolved && !level.use_step_counter)
11530 if (TimeLeft <= 10 && setup.time_limit)
11531 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11534 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11536 DisplayGameControlValues();
11538 DrawGameValue_Time(TimeLeft);
11541 if (!TimeLeft && setup.time_limit)
11543 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11544 level.native_em_level->lev->killed_out_of_time = TRUE;
11546 for (i = 0; i < MAX_PLAYERS; i++)
11547 KillPlayer(&stored_player[i]);
11551 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11553 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11555 DisplayGameControlValues();
11558 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11559 DrawGameValue_Time(TimePlayed);
11562 level.native_em_level->lev->time =
11563 (level.time == 0 ? TimePlayed : TimeLeft);
11566 if (tape.recording || tape.playing)
11567 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11571 UpdateAndDisplayGameControlValues();
11573 UpdateGameDoorValues();
11574 DrawGameDoorValues();
11578 void AdvanceFrameAndPlayerCounters(int player_nr)
11582 /* advance frame counters (global frame counter and time frame counter) */
11586 /* advance player counters (counters for move delay, move animation etc.) */
11587 for (i = 0; i < MAX_PLAYERS; i++)
11589 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11590 int move_delay_value = stored_player[i].move_delay_value;
11591 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11593 if (!advance_player_counters) /* not all players may be affected */
11596 #if USE_NEW_PLAYER_ANIM
11597 if (move_frames == 0) /* less than one move per game frame */
11599 int stepsize = TILEX / move_delay_value;
11600 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11601 int count = (stored_player[i].is_moving ?
11602 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11604 if (count % delay == 0)
11609 stored_player[i].Frame += move_frames;
11611 if (stored_player[i].MovPos != 0)
11612 stored_player[i].StepFrame += move_frames;
11614 if (stored_player[i].move_delay > 0)
11615 stored_player[i].move_delay--;
11617 /* due to bugs in previous versions, counter must count up, not down */
11618 if (stored_player[i].push_delay != -1)
11619 stored_player[i].push_delay++;
11621 if (stored_player[i].drop_delay > 0)
11622 stored_player[i].drop_delay--;
11624 if (stored_player[i].is_dropping_pressed)
11625 stored_player[i].drop_pressed_delay++;
11629 void StartGameActions(boolean init_network_game, boolean record_tape,
11632 unsigned long new_random_seed = InitRND(random_seed);
11635 TapeStartRecording(new_random_seed);
11637 #if defined(NETWORK_AVALIABLE)
11638 if (init_network_game)
11640 SendToServer_StartPlaying();
11651 static unsigned long game_frame_delay = 0;
11652 unsigned long game_frame_delay_value;
11653 byte *recorded_player_action;
11654 byte summarized_player_action = 0;
11655 byte tape_action[MAX_PLAYERS];
11658 /* detect endless loops, caused by custom element programming */
11659 if (recursion_loop_detected && recursion_loop_depth == 0)
11661 char *message = getStringCat3("Internal Error ! Element ",
11662 EL_NAME(recursion_loop_element),
11663 " caused endless loop ! Quit the game ?");
11665 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11666 EL_NAME(recursion_loop_element));
11668 RequestQuitGameExt(FALSE, level_editor_test_game, message);
11670 recursion_loop_detected = FALSE; /* if game should be continued */
11677 if (game.restart_level)
11678 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
11680 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11682 if (level.native_em_level->lev->home == 0) /* all players at home */
11684 PlayerWins(local_player);
11686 AllPlayersGone = TRUE;
11688 level.native_em_level->lev->home = -1;
11691 if (level.native_em_level->ply[0]->alive == 0 &&
11692 level.native_em_level->ply[1]->alive == 0 &&
11693 level.native_em_level->ply[2]->alive == 0 &&
11694 level.native_em_level->ply[3]->alive == 0) /* all dead */
11695 AllPlayersGone = TRUE;
11698 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11701 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11704 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
11707 game_frame_delay_value =
11708 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11710 if (tape.playing && tape.warp_forward && !tape.pausing)
11711 game_frame_delay_value = 0;
11713 /* ---------- main game synchronization point ---------- */
11715 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11717 if (network_playing && !network_player_action_received)
11719 /* try to get network player actions in time */
11721 #if defined(NETWORK_AVALIABLE)
11722 /* last chance to get network player actions without main loop delay */
11723 HandleNetworking();
11726 /* game was quit by network peer */
11727 if (game_status != GAME_MODE_PLAYING)
11730 if (!network_player_action_received)
11731 return; /* failed to get network player actions in time */
11733 /* do not yet reset "network_player_action_received" (for tape.pausing) */
11739 /* at this point we know that we really continue executing the game */
11741 network_player_action_received = FALSE;
11743 /* when playing tape, read previously recorded player input from tape data */
11744 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11747 /* TapePlayAction() may return NULL when toggling to "pause before death" */
11752 if (tape.set_centered_player)
11754 game.centered_player_nr_next = tape.centered_player_nr_next;
11755 game.set_centered_player = TRUE;
11758 for (i = 0; i < MAX_PLAYERS; i++)
11760 summarized_player_action |= stored_player[i].action;
11762 if (!network_playing)
11763 stored_player[i].effective_action = stored_player[i].action;
11766 #if defined(NETWORK_AVALIABLE)
11767 if (network_playing)
11768 SendToServer_MovePlayer(summarized_player_action);
11771 if (!options.network && !setup.team_mode)
11772 local_player->effective_action = summarized_player_action;
11774 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
11776 for (i = 0; i < MAX_PLAYERS; i++)
11777 stored_player[i].effective_action =
11778 (i == game.centered_player_nr ? summarized_player_action : 0);
11781 if (recorded_player_action != NULL)
11782 for (i = 0; i < MAX_PLAYERS; i++)
11783 stored_player[i].effective_action = recorded_player_action[i];
11785 for (i = 0; i < MAX_PLAYERS; i++)
11787 tape_action[i] = stored_player[i].effective_action;
11789 /* (this can only happen in the R'n'D game engine) */
11790 if (tape.recording && tape_action[i] && !tape.player_participates[i])
11791 tape.player_participates[i] = TRUE; /* player just appeared from CE */
11794 /* only record actions from input devices, but not programmed actions */
11795 if (tape.recording)
11796 TapeRecordAction(tape_action);
11798 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11800 GameActions_EM_Main();
11808 void GameActions_EM_Main()
11810 byte effective_action[MAX_PLAYERS];
11811 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11814 for (i = 0; i < MAX_PLAYERS; i++)
11815 effective_action[i] = stored_player[i].effective_action;
11817 GameActions_EM(effective_action, warp_mode);
11821 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11824 void GameActions_RND()
11826 int magic_wall_x = 0, magic_wall_y = 0;
11827 int i, x, y, element, graphic;
11829 InitPlayfieldScanModeVars();
11831 #if USE_ONE_MORE_CHANGE_PER_FRAME
11832 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11834 SCAN_PLAYFIELD(x, y)
11836 ChangeCount[x][y] = 0;
11837 ChangeEvent[x][y] = -1;
11842 if (game.set_centered_player)
11844 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11846 /* switching to "all players" only possible if all players fit to screen */
11847 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11849 game.centered_player_nr_next = game.centered_player_nr;
11850 game.set_centered_player = FALSE;
11853 /* do not switch focus to non-existing (or non-active) player */
11854 if (game.centered_player_nr_next >= 0 &&
11855 !stored_player[game.centered_player_nr_next].active)
11857 game.centered_player_nr_next = game.centered_player_nr;
11858 game.set_centered_player = FALSE;
11862 if (game.set_centered_player &&
11863 ScreenMovPos == 0) /* screen currently aligned at tile position */
11867 if (game.centered_player_nr_next == -1)
11869 setScreenCenteredToAllPlayers(&sx, &sy);
11873 sx = stored_player[game.centered_player_nr_next].jx;
11874 sy = stored_player[game.centered_player_nr_next].jy;
11877 game.centered_player_nr = game.centered_player_nr_next;
11878 game.set_centered_player = FALSE;
11880 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11881 DrawGameDoorValues();
11884 for (i = 0; i < MAX_PLAYERS; i++)
11886 int actual_player_action = stored_player[i].effective_action;
11889 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11890 - rnd_equinox_tetrachloride 048
11891 - rnd_equinox_tetrachloride_ii 096
11892 - rnd_emanuel_schmieg 002
11893 - doctor_sloan_ww 001, 020
11895 if (stored_player[i].MovPos == 0)
11896 CheckGravityMovement(&stored_player[i]);
11899 /* overwrite programmed action with tape action */
11900 if (stored_player[i].programmed_action)
11901 actual_player_action = stored_player[i].programmed_action;
11903 PlayerActions(&stored_player[i], actual_player_action);
11905 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11908 ScrollScreen(NULL, SCROLL_GO_ON);
11910 /* for backwards compatibility, the following code emulates a fixed bug that
11911 occured when pushing elements (causing elements that just made their last
11912 pushing step to already (if possible) make their first falling step in the
11913 same game frame, which is bad); this code is also needed to use the famous
11914 "spring push bug" which is used in older levels and might be wanted to be
11915 used also in newer levels, but in this case the buggy pushing code is only
11916 affecting the "spring" element and no other elements */
11918 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11920 for (i = 0; i < MAX_PLAYERS; i++)
11922 struct PlayerInfo *player = &stored_player[i];
11923 int x = player->jx;
11924 int y = player->jy;
11926 if (player->active && player->is_pushing && player->is_moving &&
11928 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11929 Feld[x][y] == EL_SPRING))
11931 ContinueMoving(x, y);
11933 /* continue moving after pushing (this is actually a bug) */
11934 if (!IS_MOVING(x, y))
11935 Stop[x][y] = FALSE;
11941 debug_print_timestamp(0, "start main loop profiling");
11944 SCAN_PLAYFIELD(x, y)
11946 ChangeCount[x][y] = 0;
11947 ChangeEvent[x][y] = -1;
11949 /* this must be handled before main playfield loop */
11950 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11953 if (MovDelay[x][y] <= 0)
11957 #if USE_NEW_SNAP_DELAY
11958 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11961 if (MovDelay[x][y] <= 0)
11964 DrawLevelField(x, y);
11966 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11972 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11974 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11975 printf("GameActions(): This should never happen!\n");
11977 ChangePage[x][y] = -1;
11981 Stop[x][y] = FALSE;
11982 if (WasJustMoving[x][y] > 0)
11983 WasJustMoving[x][y]--;
11984 if (WasJustFalling[x][y] > 0)
11985 WasJustFalling[x][y]--;
11986 if (CheckCollision[x][y] > 0)
11987 CheckCollision[x][y]--;
11988 if (CheckImpact[x][y] > 0)
11989 CheckImpact[x][y]--;
11993 /* reset finished pushing action (not done in ContinueMoving() to allow
11994 continuous pushing animation for elements with zero push delay) */
11995 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11997 ResetGfxAnimation(x, y);
11998 DrawLevelField(x, y);
12002 if (IS_BLOCKED(x, y))
12006 Blocked2Moving(x, y, &oldx, &oldy);
12007 if (!IS_MOVING(oldx, oldy))
12009 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12010 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12011 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12012 printf("GameActions(): This should never happen!\n");
12019 debug_print_timestamp(0, "- time for pre-main loop:");
12022 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
12023 SCAN_PLAYFIELD(x, y)
12025 element = Feld[x][y];
12026 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12031 int element2 = element;
12032 int graphic2 = graphic;
12034 int element2 = Feld[x][y];
12035 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12037 int last_gfx_frame = GfxFrame[x][y];
12039 if (graphic_info[graphic2].anim_global_sync)
12040 GfxFrame[x][y] = FrameCounter;
12041 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12042 GfxFrame[x][y] = CustomValue[x][y];
12043 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12044 GfxFrame[x][y] = element_info[element2].collect_score;
12045 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12046 GfxFrame[x][y] = ChangeDelay[x][y];
12048 if (redraw && GfxFrame[x][y] != last_gfx_frame)
12049 DrawLevelGraphicAnimation(x, y, graphic2);
12052 ResetGfxFrame(x, y, TRUE);
12056 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12057 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12058 ResetRandomAnimationValue(x, y);
12062 SetRandomAnimationValue(x, y);
12066 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12069 #endif // -------------------- !!! TEST ONLY !!! --------------------
12072 debug_print_timestamp(0, "- time for TEST loop: -->");
12075 SCAN_PLAYFIELD(x, y)
12077 element = Feld[x][y];
12078 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12080 ResetGfxFrame(x, y, TRUE);
12082 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12083 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12084 ResetRandomAnimationValue(x, y);
12086 SetRandomAnimationValue(x, y);
12088 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12090 if (IS_INACTIVE(element))
12092 if (IS_ANIMATED(graphic))
12093 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12098 /* this may take place after moving, so 'element' may have changed */
12099 if (IS_CHANGING(x, y) &&
12100 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12102 int page = element_info[element].event_page_nr[CE_DELAY];
12105 HandleElementChange(x, y, page);
12107 if (CAN_CHANGE(element))
12108 HandleElementChange(x, y, page);
12110 if (HAS_ACTION(element))
12111 ExecuteCustomElementAction(x, y, element, page);
12114 element = Feld[x][y];
12115 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12118 #if 0 // ---------------------------------------------------------------------
12120 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12124 element = Feld[x][y];
12125 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12127 if (IS_ANIMATED(graphic) &&
12128 !IS_MOVING(x, y) &&
12130 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12132 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12133 DrawTwinkleOnField(x, y);
12135 else if (IS_MOVING(x, y))
12136 ContinueMoving(x, y);
12143 case EL_EM_EXIT_OPEN:
12144 case EL_SP_EXIT_OPEN:
12145 case EL_STEEL_EXIT_OPEN:
12146 case EL_EM_STEEL_EXIT_OPEN:
12147 case EL_SP_TERMINAL:
12148 case EL_SP_TERMINAL_ACTIVE:
12149 case EL_EXTRA_TIME:
12150 case EL_SHIELD_NORMAL:
12151 case EL_SHIELD_DEADLY:
12152 if (IS_ANIMATED(graphic))
12153 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12156 case EL_DYNAMITE_ACTIVE:
12157 case EL_EM_DYNAMITE_ACTIVE:
12158 case EL_DYNABOMB_PLAYER_1_ACTIVE:
12159 case EL_DYNABOMB_PLAYER_2_ACTIVE:
12160 case EL_DYNABOMB_PLAYER_3_ACTIVE:
12161 case EL_DYNABOMB_PLAYER_4_ACTIVE:
12162 case EL_SP_DISK_RED_ACTIVE:
12163 CheckDynamite(x, y);
12166 case EL_AMOEBA_GROWING:
12167 AmoebeWaechst(x, y);
12170 case EL_AMOEBA_SHRINKING:
12171 AmoebaDisappearing(x, y);
12174 #if !USE_NEW_AMOEBA_CODE
12175 case EL_AMOEBA_WET:
12176 case EL_AMOEBA_DRY:
12177 case EL_AMOEBA_FULL:
12179 case EL_EMC_DRIPPER:
12180 AmoebeAbleger(x, y);
12184 case EL_GAME_OF_LIFE:
12189 case EL_EXIT_CLOSED:
12193 case EL_EM_EXIT_CLOSED:
12197 case EL_STEEL_EXIT_CLOSED:
12198 CheckExitSteel(x, y);
12201 case EL_EM_STEEL_EXIT_CLOSED:
12202 CheckExitSteelEM(x, y);
12205 case EL_SP_EXIT_CLOSED:
12209 case EL_EXPANDABLE_WALL_GROWING:
12210 case EL_EXPANDABLE_STEELWALL_GROWING:
12211 MauerWaechst(x, y);
12214 case EL_EXPANDABLE_WALL:
12215 case EL_EXPANDABLE_WALL_HORIZONTAL:
12216 case EL_EXPANDABLE_WALL_VERTICAL:
12217 case EL_EXPANDABLE_WALL_ANY:
12218 case EL_BD_EXPANDABLE_WALL:
12219 MauerAbleger(x, y);
12222 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12223 case EL_EXPANDABLE_STEELWALL_VERTICAL:
12224 case EL_EXPANDABLE_STEELWALL_ANY:
12225 MauerAblegerStahl(x, y);
12229 CheckForDragon(x, y);
12235 case EL_ELEMENT_SNAPPING:
12236 case EL_DIAGONAL_SHRINKING:
12237 case EL_DIAGONAL_GROWING:
12240 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12242 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12247 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12248 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12253 #else // ---------------------------------------------------------------------
12255 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12259 element = Feld[x][y];
12260 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12262 if (IS_ANIMATED(graphic) &&
12263 !IS_MOVING(x, y) &&
12265 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12267 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12268 DrawTwinkleOnField(x, y);
12270 else if ((element == EL_ACID ||
12271 element == EL_EXIT_OPEN ||
12272 element == EL_EM_EXIT_OPEN ||
12273 element == EL_SP_EXIT_OPEN ||
12274 element == EL_STEEL_EXIT_OPEN ||
12275 element == EL_EM_STEEL_EXIT_OPEN ||
12276 element == EL_SP_TERMINAL ||
12277 element == EL_SP_TERMINAL_ACTIVE ||
12278 element == EL_EXTRA_TIME ||
12279 element == EL_SHIELD_NORMAL ||
12280 element == EL_SHIELD_DEADLY) &&
12281 IS_ANIMATED(graphic))
12282 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12283 else if (IS_MOVING(x, y))
12284 ContinueMoving(x, y);
12285 else if (IS_ACTIVE_BOMB(element))
12286 CheckDynamite(x, y);
12287 else if (element == EL_AMOEBA_GROWING)
12288 AmoebeWaechst(x, y);
12289 else if (element == EL_AMOEBA_SHRINKING)
12290 AmoebaDisappearing(x, y);
12292 #if !USE_NEW_AMOEBA_CODE
12293 else if (IS_AMOEBALIVE(element))
12294 AmoebeAbleger(x, y);
12297 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12299 else if (element == EL_EXIT_CLOSED)
12301 else if (element == EL_EM_EXIT_CLOSED)
12303 else if (element == EL_STEEL_EXIT_CLOSED)
12304 CheckExitSteel(x, y);
12305 else if (element == EL_EM_STEEL_EXIT_CLOSED)
12306 CheckExitSteelEM(x, y);
12307 else if (element == EL_SP_EXIT_CLOSED)
12309 else if (element == EL_EXPANDABLE_WALL_GROWING ||
12310 element == EL_EXPANDABLE_STEELWALL_GROWING)
12311 MauerWaechst(x, y);
12312 else if (element == EL_EXPANDABLE_WALL ||
12313 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12314 element == EL_EXPANDABLE_WALL_VERTICAL ||
12315 element == EL_EXPANDABLE_WALL_ANY ||
12316 element == EL_BD_EXPANDABLE_WALL)
12317 MauerAbleger(x, y);
12318 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12319 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12320 element == EL_EXPANDABLE_STEELWALL_ANY)
12321 MauerAblegerStahl(x, y);
12322 else if (element == EL_FLAMES)
12323 CheckForDragon(x, y);
12324 else if (element == EL_EXPLOSION)
12325 ; /* drawing of correct explosion animation is handled separately */
12326 else if (element == EL_ELEMENT_SNAPPING ||
12327 element == EL_DIAGONAL_SHRINKING ||
12328 element == EL_DIAGONAL_GROWING)
12330 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12332 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12334 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12335 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12337 #endif // ---------------------------------------------------------------------
12339 if (IS_BELT_ACTIVE(element))
12340 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12342 if (game.magic_wall_active)
12344 int jx = local_player->jx, jy = local_player->jy;
12346 /* play the element sound at the position nearest to the player */
12347 if ((element == EL_MAGIC_WALL_FULL ||
12348 element == EL_MAGIC_WALL_ACTIVE ||
12349 element == EL_MAGIC_WALL_EMPTYING ||
12350 element == EL_BD_MAGIC_WALL_FULL ||
12351 element == EL_BD_MAGIC_WALL_ACTIVE ||
12352 element == EL_BD_MAGIC_WALL_EMPTYING ||
12353 element == EL_DC_MAGIC_WALL_FULL ||
12354 element == EL_DC_MAGIC_WALL_ACTIVE ||
12355 element == EL_DC_MAGIC_WALL_EMPTYING) &&
12356 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12365 debug_print_timestamp(0, "- time for MAIN loop: -->");
12368 #if USE_NEW_AMOEBA_CODE
12369 /* new experimental amoeba growth stuff */
12370 if (!(FrameCounter % 8))
12372 static unsigned long random = 1684108901;
12374 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12376 x = RND(lev_fieldx);
12377 y = RND(lev_fieldy);
12378 element = Feld[x][y];
12380 if (!IS_PLAYER(x,y) &&
12381 (element == EL_EMPTY ||
12382 CAN_GROW_INTO(element) ||
12383 element == EL_QUICKSAND_EMPTY ||
12384 element == EL_QUICKSAND_FAST_EMPTY ||
12385 element == EL_ACID_SPLASH_LEFT ||
12386 element == EL_ACID_SPLASH_RIGHT))
12388 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12389 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12390 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12391 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12392 Feld[x][y] = EL_AMOEBA_DROP;
12395 random = random * 129 + 1;
12401 if (game.explosions_delayed)
12404 game.explosions_delayed = FALSE;
12406 SCAN_PLAYFIELD(x, y)
12408 element = Feld[x][y];
12410 if (ExplodeField[x][y])
12411 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12412 else if (element == EL_EXPLOSION)
12413 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12415 ExplodeField[x][y] = EX_TYPE_NONE;
12418 game.explosions_delayed = TRUE;
12421 if (game.magic_wall_active)
12423 if (!(game.magic_wall_time_left % 4))
12425 int element = Feld[magic_wall_x][magic_wall_y];
12427 if (element == EL_BD_MAGIC_WALL_FULL ||
12428 element == EL_BD_MAGIC_WALL_ACTIVE ||
12429 element == EL_BD_MAGIC_WALL_EMPTYING)
12430 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12431 else if (element == EL_DC_MAGIC_WALL_FULL ||
12432 element == EL_DC_MAGIC_WALL_ACTIVE ||
12433 element == EL_DC_MAGIC_WALL_EMPTYING)
12434 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12436 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12439 if (game.magic_wall_time_left > 0)
12441 game.magic_wall_time_left--;
12443 if (!game.magic_wall_time_left)
12445 SCAN_PLAYFIELD(x, y)
12447 element = Feld[x][y];
12449 if (element == EL_MAGIC_WALL_ACTIVE ||
12450 element == EL_MAGIC_WALL_FULL)
12452 Feld[x][y] = EL_MAGIC_WALL_DEAD;
12453 DrawLevelField(x, y);
12455 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12456 element == EL_BD_MAGIC_WALL_FULL)
12458 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12459 DrawLevelField(x, y);
12461 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12462 element == EL_DC_MAGIC_WALL_FULL)
12464 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12465 DrawLevelField(x, y);
12469 game.magic_wall_active = FALSE;
12474 if (game.light_time_left > 0)
12476 game.light_time_left--;
12478 if (game.light_time_left == 0)
12479 RedrawAllLightSwitchesAndInvisibleElements();
12482 if (game.timegate_time_left > 0)
12484 game.timegate_time_left--;
12486 if (game.timegate_time_left == 0)
12487 CloseAllOpenTimegates();
12490 if (game.lenses_time_left > 0)
12492 game.lenses_time_left--;
12494 if (game.lenses_time_left == 0)
12495 RedrawAllInvisibleElementsForLenses();
12498 if (game.magnify_time_left > 0)
12500 game.magnify_time_left--;
12502 if (game.magnify_time_left == 0)
12503 RedrawAllInvisibleElementsForMagnifier();
12506 for (i = 0; i < MAX_PLAYERS; i++)
12508 struct PlayerInfo *player = &stored_player[i];
12510 if (SHIELD_ON(player))
12512 if (player->shield_deadly_time_left)
12513 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12514 else if (player->shield_normal_time_left)
12515 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12522 PlayAllPlayersSound();
12524 if (options.debug) /* calculate frames per second */
12526 static unsigned long fps_counter = 0;
12527 static int fps_frames = 0;
12528 unsigned long fps_delay_ms = Counter() - fps_counter;
12532 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
12534 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12537 fps_counter = Counter();
12540 redraw_mask |= REDRAW_FPS;
12543 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12545 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12547 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12549 local_player->show_envelope = 0;
12553 debug_print_timestamp(0, "stop main loop profiling ");
12554 printf("----------------------------------------------------------\n");
12557 /* use random number generator in every frame to make it less predictable */
12558 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12562 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12564 int min_x = x, min_y = y, max_x = x, max_y = y;
12567 for (i = 0; i < MAX_PLAYERS; i++)
12569 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12571 if (!stored_player[i].active || &stored_player[i] == player)
12574 min_x = MIN(min_x, jx);
12575 min_y = MIN(min_y, jy);
12576 max_x = MAX(max_x, jx);
12577 max_y = MAX(max_y, jy);
12580 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12583 static boolean AllPlayersInVisibleScreen()
12587 for (i = 0; i < MAX_PLAYERS; i++)
12589 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12591 if (!stored_player[i].active)
12594 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12601 void ScrollLevel(int dx, int dy)
12604 static Bitmap *bitmap_db_field2 = NULL;
12605 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12612 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
12613 /* only horizontal XOR vertical scroll direction allowed */
12614 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
12619 if (bitmap_db_field2 == NULL)
12620 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
12622 /* needed when blitting directly to same bitmap -- should not be needed with
12623 recent SDL libraries, but apparently does not work in 1.2.11 directly */
12624 BlitBitmap(drawto_field, bitmap_db_field2,
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);
12631 BlitBitmap(bitmap_db_field2, drawto_field,
12632 FX + TILEX * (dx == 1) - softscroll_offset,
12633 FY + TILEY * (dy == 1) - softscroll_offset,
12634 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12635 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12636 FX + TILEX * (dx == 1) - softscroll_offset,
12637 FY + TILEY * (dy == 1) - softscroll_offset);
12642 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
12643 int xsize = (BX2 - BX1 + 1);
12644 int ysize = (BY2 - BY1 + 1);
12645 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
12646 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
12647 int step = (start < end ? +1 : -1);
12649 for (i = start; i != end; i += step)
12651 BlitBitmap(drawto_field, drawto_field,
12652 FX + TILEX * (dx != 0 ? i + step : 0),
12653 FY + TILEY * (dy != 0 ? i + step : 0),
12654 TILEX * (dx != 0 ? 1 : xsize),
12655 TILEY * (dy != 0 ? 1 : ysize),
12656 FX + TILEX * (dx != 0 ? i : 0),
12657 FY + TILEY * (dy != 0 ? i : 0));
12662 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12664 BlitBitmap(drawto_field, drawto_field,
12665 FX + TILEX * (dx == -1) - softscroll_offset,
12666 FY + TILEY * (dy == -1) - softscroll_offset,
12667 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12668 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12669 FX + TILEX * (dx == 1) - softscroll_offset,
12670 FY + TILEY * (dy == 1) - softscroll_offset);
12676 x = (dx == 1 ? BX1 : BX2);
12677 for (y = BY1; y <= BY2; y++)
12678 DrawScreenField(x, y);
12683 y = (dy == 1 ? BY1 : BY2);
12684 for (x = BX1; x <= BX2; x++)
12685 DrawScreenField(x, y);
12688 redraw_mask |= REDRAW_FIELD;
12691 static boolean canFallDown(struct PlayerInfo *player)
12693 int jx = player->jx, jy = player->jy;
12695 return (IN_LEV_FIELD(jx, jy + 1) &&
12696 (IS_FREE(jx, jy + 1) ||
12697 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12698 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12699 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12702 static boolean canPassField(int x, int y, int move_dir)
12704 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12705 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12706 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12707 int nextx = x + dx;
12708 int nexty = y + dy;
12709 int element = Feld[x][y];
12711 return (IS_PASSABLE_FROM(element, opposite_dir) &&
12712 !CAN_MOVE(element) &&
12713 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12714 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12715 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12718 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12720 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12721 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12722 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12726 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12727 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12728 (IS_DIGGABLE(Feld[newx][newy]) ||
12729 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12730 canPassField(newx, newy, move_dir)));
12733 static void CheckGravityMovement(struct PlayerInfo *player)
12735 #if USE_PLAYER_GRAVITY
12736 if (player->gravity && !player->programmed_action)
12738 if (game.gravity && !player->programmed_action)
12741 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12742 int move_dir_vertical = player->effective_action & MV_VERTICAL;
12743 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12744 int jx = player->jx, jy = player->jy;
12745 boolean player_is_moving_to_valid_field =
12746 (!player_is_snapping &&
12747 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12748 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12749 boolean player_can_fall_down = canFallDown(player);
12751 if (player_can_fall_down &&
12752 !player_is_moving_to_valid_field)
12753 player->programmed_action = MV_DOWN;
12757 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12759 return CheckGravityMovement(player);
12761 #if USE_PLAYER_GRAVITY
12762 if (player->gravity && !player->programmed_action)
12764 if (game.gravity && !player->programmed_action)
12767 int jx = player->jx, jy = player->jy;
12768 boolean field_under_player_is_free =
12769 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12770 boolean player_is_standing_on_valid_field =
12771 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12772 (IS_WALKABLE(Feld[jx][jy]) &&
12773 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12775 if (field_under_player_is_free && !player_is_standing_on_valid_field)
12776 player->programmed_action = MV_DOWN;
12781 MovePlayerOneStep()
12782 -----------------------------------------------------------------------------
12783 dx, dy: direction (non-diagonal) to try to move the player to
12784 real_dx, real_dy: direction as read from input device (can be diagonal)
12787 boolean MovePlayerOneStep(struct PlayerInfo *player,
12788 int dx, int dy, int real_dx, int real_dy)
12790 int jx = player->jx, jy = player->jy;
12791 int new_jx = jx + dx, new_jy = jy + dy;
12792 #if !USE_FIXED_DONT_RUN_INTO
12796 boolean player_can_move = !player->cannot_move;
12798 if (!player->active || (!dx && !dy))
12799 return MP_NO_ACTION;
12801 player->MovDir = (dx < 0 ? MV_LEFT :
12802 dx > 0 ? MV_RIGHT :
12804 dy > 0 ? MV_DOWN : MV_NONE);
12806 if (!IN_LEV_FIELD(new_jx, new_jy))
12807 return MP_NO_ACTION;
12809 if (!player_can_move)
12811 if (player->MovPos == 0)
12813 player->is_moving = FALSE;
12814 player->is_digging = FALSE;
12815 player->is_collecting = FALSE;
12816 player->is_snapping = FALSE;
12817 player->is_pushing = FALSE;
12822 if (!options.network && game.centered_player_nr == -1 &&
12823 !AllPlayersInSight(player, new_jx, new_jy))
12824 return MP_NO_ACTION;
12826 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
12827 return MP_NO_ACTION;
12830 #if !USE_FIXED_DONT_RUN_INTO
12831 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
12833 /* (moved to DigField()) */
12834 if (player_can_move && DONT_RUN_INTO(element))
12836 if (element == EL_ACID && dx == 0 && dy == 1)
12838 SplashAcid(new_jx, new_jy);
12839 Feld[jx][jy] = EL_PLAYER_1;
12840 InitMovingField(jx, jy, MV_DOWN);
12841 Store[jx][jy] = EL_ACID;
12842 ContinueMoving(jx, jy);
12843 BuryPlayer(player);
12846 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12852 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12853 if (can_move != MP_MOVING)
12856 /* check if DigField() has caused relocation of the player */
12857 if (player->jx != jx || player->jy != jy)
12858 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12860 StorePlayer[jx][jy] = 0;
12861 player->last_jx = jx;
12862 player->last_jy = jy;
12863 player->jx = new_jx;
12864 player->jy = new_jy;
12865 StorePlayer[new_jx][new_jy] = player->element_nr;
12867 if (player->move_delay_value_next != -1)
12869 player->move_delay_value = player->move_delay_value_next;
12870 player->move_delay_value_next = -1;
12874 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12876 player->step_counter++;
12878 PlayerVisit[jx][jy] = FrameCounter;
12880 #if USE_UFAST_PLAYER_EXIT_BUGFIX
12881 player->is_moving = TRUE;
12885 /* should better be called in MovePlayer(), but this breaks some tapes */
12886 ScrollPlayer(player, SCROLL_INIT);
12892 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12894 int jx = player->jx, jy = player->jy;
12895 int old_jx = jx, old_jy = jy;
12896 int moved = MP_NO_ACTION;
12898 if (!player->active)
12903 if (player->MovPos == 0)
12905 player->is_moving = FALSE;
12906 player->is_digging = FALSE;
12907 player->is_collecting = FALSE;
12908 player->is_snapping = FALSE;
12909 player->is_pushing = FALSE;
12915 if (player->move_delay > 0)
12918 player->move_delay = -1; /* set to "uninitialized" value */
12920 /* store if player is automatically moved to next field */
12921 player->is_auto_moving = (player->programmed_action != MV_NONE);
12923 /* remove the last programmed player action */
12924 player->programmed_action = 0;
12926 if (player->MovPos)
12928 /* should only happen if pre-1.2 tape recordings are played */
12929 /* this is only for backward compatibility */
12931 int original_move_delay_value = player->move_delay_value;
12934 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
12938 /* scroll remaining steps with finest movement resolution */
12939 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12941 while (player->MovPos)
12943 ScrollPlayer(player, SCROLL_GO_ON);
12944 ScrollScreen(NULL, SCROLL_GO_ON);
12946 AdvanceFrameAndPlayerCounters(player->index_nr);
12952 player->move_delay_value = original_move_delay_value;
12955 player->is_active = FALSE;
12957 if (player->last_move_dir & MV_HORIZONTAL)
12959 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12960 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12964 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12965 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12968 #if USE_FIXED_BORDER_RUNNING_GFX
12969 if (!moved && !player->is_active)
12971 player->is_moving = FALSE;
12972 player->is_digging = FALSE;
12973 player->is_collecting = FALSE;
12974 player->is_snapping = FALSE;
12975 player->is_pushing = FALSE;
12983 if (moved & MP_MOVING && !ScreenMovPos &&
12984 (player->index_nr == game.centered_player_nr ||
12985 game.centered_player_nr == -1))
12987 if (moved & MP_MOVING && !ScreenMovPos &&
12988 (player == local_player || !options.network))
12991 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12992 int offset = game.scroll_delay_value;
12994 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12996 /* actual player has left the screen -- scroll in that direction */
12997 if (jx != old_jx) /* player has moved horizontally */
12998 scroll_x += (jx - old_jx);
12999 else /* player has moved vertically */
13000 scroll_y += (jy - old_jy);
13004 if (jx != old_jx) /* player has moved horizontally */
13006 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
13007 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13008 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13010 /* don't scroll over playfield boundaries */
13011 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13012 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13014 /* don't scroll more than one field at a time */
13015 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13017 /* don't scroll against the player's moving direction */
13018 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
13019 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13020 scroll_x = old_scroll_x;
13022 else /* player has moved vertically */
13024 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
13025 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13026 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13028 /* don't scroll over playfield boundaries */
13029 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13030 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13032 /* don't scroll more than one field at a time */
13033 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13035 /* don't scroll against the player's moving direction */
13036 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
13037 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13038 scroll_y = old_scroll_y;
13042 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13045 if (!options.network && game.centered_player_nr == -1 &&
13046 !AllPlayersInVisibleScreen())
13048 scroll_x = old_scroll_x;
13049 scroll_y = old_scroll_y;
13053 if (!options.network && !AllPlayersInVisibleScreen())
13055 scroll_x = old_scroll_x;
13056 scroll_y = old_scroll_y;
13061 ScrollScreen(player, SCROLL_INIT);
13062 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13067 player->StepFrame = 0;
13069 if (moved & MP_MOVING)
13071 if (old_jx != jx && old_jy == jy)
13072 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13073 else if (old_jx == jx && old_jy != jy)
13074 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13076 DrawLevelField(jx, jy); /* for "crumbled sand" */
13078 player->last_move_dir = player->MovDir;
13079 player->is_moving = TRUE;
13080 player->is_snapping = FALSE;
13081 player->is_switching = FALSE;
13082 player->is_dropping = FALSE;
13083 player->is_dropping_pressed = FALSE;
13084 player->drop_pressed_delay = 0;
13087 /* should better be called here than above, but this breaks some tapes */
13088 ScrollPlayer(player, SCROLL_INIT);
13093 CheckGravityMovementWhenNotMoving(player);
13095 player->is_moving = FALSE;
13097 /* at this point, the player is allowed to move, but cannot move right now
13098 (e.g. because of something blocking the way) -- ensure that the player
13099 is also allowed to move in the next frame (in old versions before 3.1.1,
13100 the player was forced to wait again for eight frames before next try) */
13102 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13103 player->move_delay = 0; /* allow direct movement in the next frame */
13106 if (player->move_delay == -1) /* not yet initialized by DigField() */
13107 player->move_delay = player->move_delay_value;
13109 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13111 TestIfPlayerTouchesBadThing(jx, jy);
13112 TestIfPlayerTouchesCustomElement(jx, jy);
13115 if (!player->active)
13116 RemovePlayer(player);
13121 void ScrollPlayer(struct PlayerInfo *player, int mode)
13123 int jx = player->jx, jy = player->jy;
13124 int last_jx = player->last_jx, last_jy = player->last_jy;
13125 int move_stepsize = TILEX / player->move_delay_value;
13127 #if USE_NEW_PLAYER_SPEED
13128 if (!player->active)
13131 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
13134 if (!player->active || player->MovPos == 0)
13138 if (mode == SCROLL_INIT)
13140 player->actual_frame_counter = FrameCounter;
13141 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13143 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13144 Feld[last_jx][last_jy] == EL_EMPTY)
13146 int last_field_block_delay = 0; /* start with no blocking at all */
13147 int block_delay_adjustment = player->block_delay_adjustment;
13149 /* if player blocks last field, add delay for exactly one move */
13150 if (player->block_last_field)
13152 last_field_block_delay += player->move_delay_value;
13154 /* when blocking enabled, prevent moving up despite gravity */
13155 #if USE_PLAYER_GRAVITY
13156 if (player->gravity && player->MovDir == MV_UP)
13157 block_delay_adjustment = -1;
13159 if (game.gravity && player->MovDir == MV_UP)
13160 block_delay_adjustment = -1;
13164 /* add block delay adjustment (also possible when not blocking) */
13165 last_field_block_delay += block_delay_adjustment;
13167 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13168 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13171 #if USE_NEW_PLAYER_SPEED
13172 if (player->MovPos != 0) /* player has not yet reached destination */
13178 else if (!FrameReached(&player->actual_frame_counter, 1))
13181 #if USE_NEW_PLAYER_SPEED
13182 if (player->MovPos != 0)
13184 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13185 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13187 /* before DrawPlayer() to draw correct player graphic for this case */
13188 if (player->MovPos == 0)
13189 CheckGravityMovement(player);
13192 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13193 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13195 /* before DrawPlayer() to draw correct player graphic for this case */
13196 if (player->MovPos == 0)
13197 CheckGravityMovement(player);
13200 if (player->MovPos == 0) /* player reached destination field */
13202 if (player->move_delay_reset_counter > 0)
13204 player->move_delay_reset_counter--;
13206 if (player->move_delay_reset_counter == 0)
13208 /* continue with normal speed after quickly moving through gate */
13209 HALVE_PLAYER_SPEED(player);
13211 /* be able to make the next move without delay */
13212 player->move_delay = 0;
13216 player->last_jx = jx;
13217 player->last_jy = jy;
13219 if (Feld[jx][jy] == EL_EXIT_OPEN ||
13220 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13221 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13222 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13223 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13224 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
13226 DrawPlayer(player); /* needed here only to cleanup last field */
13227 RemovePlayer(player);
13229 if (local_player->friends_still_needed == 0 ||
13230 IS_SP_ELEMENT(Feld[jx][jy]))
13231 PlayerWins(player);
13234 /* this breaks one level: "machine", level 000 */
13236 int move_direction = player->MovDir;
13237 int enter_side = MV_DIR_OPPOSITE(move_direction);
13238 int leave_side = move_direction;
13239 int old_jx = last_jx;
13240 int old_jy = last_jy;
13241 int old_element = Feld[old_jx][old_jy];
13242 int new_element = Feld[jx][jy];
13244 if (IS_CUSTOM_ELEMENT(old_element))
13245 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13247 player->index_bit, leave_side);
13249 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13250 CE_PLAYER_LEAVES_X,
13251 player->index_bit, leave_side);
13253 if (IS_CUSTOM_ELEMENT(new_element))
13254 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13255 player->index_bit, enter_side);
13257 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13258 CE_PLAYER_ENTERS_X,
13259 player->index_bit, enter_side);
13261 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13262 CE_MOVE_OF_X, move_direction);
13265 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13267 TestIfPlayerTouchesBadThing(jx, jy);
13268 TestIfPlayerTouchesCustomElement(jx, jy);
13270 /* needed because pushed element has not yet reached its destination,
13271 so it would trigger a change event at its previous field location */
13272 if (!player->is_pushing)
13273 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
13275 if (!player->active)
13276 RemovePlayer(player);
13279 if (!local_player->LevelSolved && level.use_step_counter)
13289 if (TimeLeft <= 10 && setup.time_limit)
13290 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13293 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13295 DisplayGameControlValues();
13297 DrawGameValue_Time(TimeLeft);
13300 if (!TimeLeft && setup.time_limit)
13301 for (i = 0; i < MAX_PLAYERS; i++)
13302 KillPlayer(&stored_player[i]);
13305 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13307 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13309 DisplayGameControlValues();
13312 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13313 DrawGameValue_Time(TimePlayed);
13317 if (tape.single_step && tape.recording && !tape.pausing &&
13318 !player->programmed_action)
13319 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13323 void ScrollScreen(struct PlayerInfo *player, int mode)
13325 static unsigned long screen_frame_counter = 0;
13327 if (mode == SCROLL_INIT)
13329 /* set scrolling step size according to actual player's moving speed */
13330 ScrollStepSize = TILEX / player->move_delay_value;
13332 screen_frame_counter = FrameCounter;
13333 ScreenMovDir = player->MovDir;
13334 ScreenMovPos = player->MovPos;
13335 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13338 else if (!FrameReached(&screen_frame_counter, 1))
13343 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13344 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13345 redraw_mask |= REDRAW_FIELD;
13348 ScreenMovDir = MV_NONE;
13351 void TestIfPlayerTouchesCustomElement(int x, int y)
13353 static int xy[4][2] =
13360 static int trigger_sides[4][2] =
13362 /* center side border side */
13363 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13364 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13365 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13366 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13368 static int touch_dir[4] =
13370 MV_LEFT | MV_RIGHT,
13375 int center_element = Feld[x][y]; /* should always be non-moving! */
13378 for (i = 0; i < NUM_DIRECTIONS; i++)
13380 int xx = x + xy[i][0];
13381 int yy = y + xy[i][1];
13382 int center_side = trigger_sides[i][0];
13383 int border_side = trigger_sides[i][1];
13384 int border_element;
13386 if (!IN_LEV_FIELD(xx, yy))
13389 if (IS_PLAYER(x, y))
13391 struct PlayerInfo *player = PLAYERINFO(x, y);
13393 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13394 border_element = Feld[xx][yy]; /* may be moving! */
13395 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13396 border_element = Feld[xx][yy];
13397 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13398 border_element = MovingOrBlocked2Element(xx, yy);
13400 continue; /* center and border element do not touch */
13402 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13403 player->index_bit, border_side);
13404 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13405 CE_PLAYER_TOUCHES_X,
13406 player->index_bit, border_side);
13408 else if (IS_PLAYER(xx, yy))
13410 struct PlayerInfo *player = PLAYERINFO(xx, yy);
13412 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13414 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13415 continue; /* center and border element do not touch */
13418 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13419 player->index_bit, center_side);
13420 CheckTriggeredElementChangeByPlayer(x, y, center_element,
13421 CE_PLAYER_TOUCHES_X,
13422 player->index_bit, center_side);
13428 #if USE_ELEMENT_TOUCHING_BUGFIX
13430 void TestIfElementTouchesCustomElement(int x, int y)
13432 static int xy[4][2] =
13439 static int trigger_sides[4][2] =
13441 /* center side border side */
13442 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13443 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13444 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13445 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13447 static int touch_dir[4] =
13449 MV_LEFT | MV_RIGHT,
13454 boolean change_center_element = FALSE;
13455 int center_element = Feld[x][y]; /* should always be non-moving! */
13456 int border_element_old[NUM_DIRECTIONS];
13459 for (i = 0; i < NUM_DIRECTIONS; i++)
13461 int xx = x + xy[i][0];
13462 int yy = y + xy[i][1];
13463 int border_element;
13465 border_element_old[i] = -1;
13467 if (!IN_LEV_FIELD(xx, yy))
13470 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13471 border_element = Feld[xx][yy]; /* may be moving! */
13472 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13473 border_element = Feld[xx][yy];
13474 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13475 border_element = MovingOrBlocked2Element(xx, yy);
13477 continue; /* center and border element do not touch */
13479 border_element_old[i] = border_element;
13482 for (i = 0; i < NUM_DIRECTIONS; i++)
13484 int xx = x + xy[i][0];
13485 int yy = y + xy[i][1];
13486 int center_side = trigger_sides[i][0];
13487 int border_element = border_element_old[i];
13489 if (border_element == -1)
13492 /* check for change of border element */
13493 CheckElementChangeBySide(xx, yy, border_element, center_element,
13494 CE_TOUCHING_X, center_side);
13497 for (i = 0; i < NUM_DIRECTIONS; i++)
13499 int border_side = trigger_sides[i][1];
13500 int border_element = border_element_old[i];
13502 if (border_element == -1)
13505 /* check for change of center element (but change it only once) */
13506 if (!change_center_element)
13507 change_center_element =
13508 CheckElementChangeBySide(x, y, center_element, border_element,
13509 CE_TOUCHING_X, border_side);
13515 void TestIfElementTouchesCustomElement_OLD(int x, int y)
13517 static int xy[4][2] =
13524 static int trigger_sides[4][2] =
13526 /* center side border side */
13527 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13528 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13529 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13530 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13532 static int touch_dir[4] =
13534 MV_LEFT | MV_RIGHT,
13539 boolean change_center_element = FALSE;
13540 int center_element = Feld[x][y]; /* should always be non-moving! */
13543 for (i = 0; i < NUM_DIRECTIONS; i++)
13545 int xx = x + xy[i][0];
13546 int yy = y + xy[i][1];
13547 int center_side = trigger_sides[i][0];
13548 int border_side = trigger_sides[i][1];
13549 int border_element;
13551 if (!IN_LEV_FIELD(xx, yy))
13554 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13555 border_element = Feld[xx][yy]; /* may be moving! */
13556 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13557 border_element = Feld[xx][yy];
13558 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13559 border_element = MovingOrBlocked2Element(xx, yy);
13561 continue; /* center and border element do not touch */
13563 /* check for change of center element (but change it only once) */
13564 if (!change_center_element)
13565 change_center_element =
13566 CheckElementChangeBySide(x, y, center_element, border_element,
13567 CE_TOUCHING_X, border_side);
13569 /* check for change of border element */
13570 CheckElementChangeBySide(xx, yy, border_element, center_element,
13571 CE_TOUCHING_X, center_side);
13577 void TestIfElementHitsCustomElement(int x, int y, int direction)
13579 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13580 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
13581 int hitx = x + dx, hity = y + dy;
13582 int hitting_element = Feld[x][y];
13583 int touched_element;
13585 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13588 touched_element = (IN_LEV_FIELD(hitx, hity) ?
13589 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13591 if (IN_LEV_FIELD(hitx, hity))
13593 int opposite_direction = MV_DIR_OPPOSITE(direction);
13594 int hitting_side = direction;
13595 int touched_side = opposite_direction;
13596 boolean object_hit = (!IS_MOVING(hitx, hity) ||
13597 MovDir[hitx][hity] != direction ||
13598 ABS(MovPos[hitx][hity]) <= TILEY / 2);
13604 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13605 CE_HITTING_X, touched_side);
13607 CheckElementChangeBySide(hitx, hity, touched_element,
13608 hitting_element, CE_HIT_BY_X, hitting_side);
13610 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13611 CE_HIT_BY_SOMETHING, opposite_direction);
13615 /* "hitting something" is also true when hitting the playfield border */
13616 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13617 CE_HITTING_SOMETHING, direction);
13621 void TestIfElementSmashesCustomElement(int x, int y, int direction)
13623 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13624 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
13625 int hitx = x + dx, hity = y + dy;
13626 int hitting_element = Feld[x][y];
13627 int touched_element;
13629 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
13630 !IS_FREE(hitx, hity) &&
13631 (!IS_MOVING(hitx, hity) ||
13632 MovDir[hitx][hity] != direction ||
13633 ABS(MovPos[hitx][hity]) <= TILEY / 2));
13636 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13640 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
13644 touched_element = (IN_LEV_FIELD(hitx, hity) ?
13645 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13647 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13648 EP_CAN_SMASH_EVERYTHING, direction);
13650 if (IN_LEV_FIELD(hitx, hity))
13652 int opposite_direction = MV_DIR_OPPOSITE(direction);
13653 int hitting_side = direction;
13654 int touched_side = opposite_direction;
13656 int touched_element = MovingOrBlocked2Element(hitx, hity);
13659 boolean object_hit = (!IS_MOVING(hitx, hity) ||
13660 MovDir[hitx][hity] != direction ||
13661 ABS(MovPos[hitx][hity]) <= TILEY / 2);
13670 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13671 CE_SMASHED_BY_SOMETHING, opposite_direction);
13673 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13674 CE_OTHER_IS_SMASHING, touched_side);
13676 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13677 CE_OTHER_GETS_SMASHED, hitting_side);
13683 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13685 int i, kill_x = -1, kill_y = -1;
13687 int bad_element = -1;
13688 static int test_xy[4][2] =
13695 static int test_dir[4] =
13703 for (i = 0; i < NUM_DIRECTIONS; i++)
13705 int test_x, test_y, test_move_dir, test_element;
13707 test_x = good_x + test_xy[i][0];
13708 test_y = good_y + test_xy[i][1];
13710 if (!IN_LEV_FIELD(test_x, test_y))
13714 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13716 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13718 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13719 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13721 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13722 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
13726 bad_element = test_element;
13732 if (kill_x != -1 || kill_y != -1)
13734 if (IS_PLAYER(good_x, good_y))
13736 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13738 if (player->shield_deadly_time_left > 0 &&
13739 !IS_INDESTRUCTIBLE(bad_element))
13740 Bang(kill_x, kill_y);
13741 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13742 KillPlayer(player);
13745 Bang(good_x, good_y);
13749 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13751 int i, kill_x = -1, kill_y = -1;
13752 int bad_element = Feld[bad_x][bad_y];
13753 static int test_xy[4][2] =
13760 static int touch_dir[4] =
13762 MV_LEFT | MV_RIGHT,
13767 static int test_dir[4] =
13775 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
13778 for (i = 0; i < NUM_DIRECTIONS; i++)
13780 int test_x, test_y, test_move_dir, test_element;
13782 test_x = bad_x + test_xy[i][0];
13783 test_y = bad_y + test_xy[i][1];
13784 if (!IN_LEV_FIELD(test_x, test_y))
13788 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13790 test_element = Feld[test_x][test_y];
13792 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13793 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13795 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
13796 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
13798 /* good thing is player or penguin that does not move away */
13799 if (IS_PLAYER(test_x, test_y))
13801 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13803 if (bad_element == EL_ROBOT && player->is_moving)
13804 continue; /* robot does not kill player if he is moving */
13806 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13808 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13809 continue; /* center and border element do not touch */
13816 else if (test_element == EL_PENGUIN)
13825 if (kill_x != -1 || kill_y != -1)
13827 if (IS_PLAYER(kill_x, kill_y))
13829 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13831 if (player->shield_deadly_time_left > 0 &&
13832 !IS_INDESTRUCTIBLE(bad_element))
13833 Bang(bad_x, bad_y);
13834 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13835 KillPlayer(player);
13838 Bang(kill_x, kill_y);
13842 void TestIfPlayerTouchesBadThing(int x, int y)
13844 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13847 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13849 TestIfGoodThingHitsBadThing(x, y, move_dir);
13852 void TestIfBadThingTouchesPlayer(int x, int y)
13854 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13857 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13859 TestIfBadThingHitsGoodThing(x, y, move_dir);
13862 void TestIfFriendTouchesBadThing(int x, int y)
13864 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13867 void TestIfBadThingTouchesFriend(int x, int y)
13869 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13872 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13874 int i, kill_x = bad_x, kill_y = bad_y;
13875 static int xy[4][2] =
13883 for (i = 0; i < NUM_DIRECTIONS; i++)
13887 x = bad_x + xy[i][0];
13888 y = bad_y + xy[i][1];
13889 if (!IN_LEV_FIELD(x, y))
13892 element = Feld[x][y];
13893 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13894 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13902 if (kill_x != bad_x || kill_y != bad_y)
13903 Bang(bad_x, bad_y);
13906 void KillPlayer(struct PlayerInfo *player)
13908 int jx = player->jx, jy = player->jy;
13910 if (!player->active)
13913 /* the following code was introduced to prevent an infinite loop when calling
13915 -> CheckTriggeredElementChangeExt()
13916 -> ExecuteCustomElementAction()
13918 -> (infinitely repeating the above sequence of function calls)
13919 which occurs when killing the player while having a CE with the setting
13920 "kill player X when explosion of <player X>"; the solution using a new
13921 field "player->killed" was chosen for backwards compatibility, although
13922 clever use of the fields "player->active" etc. would probably also work */
13924 if (player->killed)
13928 player->killed = TRUE;
13930 /* remove accessible field at the player's position */
13931 Feld[jx][jy] = EL_EMPTY;
13933 /* deactivate shield (else Bang()/Explode() would not work right) */
13934 player->shield_normal_time_left = 0;
13935 player->shield_deadly_time_left = 0;
13938 BuryPlayer(player);
13941 static void KillPlayerUnlessEnemyProtected(int x, int y)
13943 if (!PLAYER_ENEMY_PROTECTED(x, y))
13944 KillPlayer(PLAYERINFO(x, y));
13947 static void KillPlayerUnlessExplosionProtected(int x, int y)
13949 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13950 KillPlayer(PLAYERINFO(x, y));
13953 void BuryPlayer(struct PlayerInfo *player)
13955 int jx = player->jx, jy = player->jy;
13957 if (!player->active)
13960 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13961 PlayLevelSound(jx, jy, SND_GAME_LOSING);
13963 player->GameOver = TRUE;
13964 RemovePlayer(player);
13967 void RemovePlayer(struct PlayerInfo *player)
13969 int jx = player->jx, jy = player->jy;
13970 int i, found = FALSE;
13972 player->present = FALSE;
13973 player->active = FALSE;
13975 if (!ExplodeField[jx][jy])
13976 StorePlayer[jx][jy] = 0;
13978 if (player->is_moving)
13979 DrawLevelField(player->last_jx, player->last_jy);
13981 for (i = 0; i < MAX_PLAYERS; i++)
13982 if (stored_player[i].active)
13986 AllPlayersGone = TRUE;
13992 #if USE_NEW_SNAP_DELAY
13993 static void setFieldForSnapping(int x, int y, int element, int direction)
13995 struct ElementInfo *ei = &element_info[element];
13996 int direction_bit = MV_DIR_TO_BIT(direction);
13997 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13998 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13999 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14001 Feld[x][y] = EL_ELEMENT_SNAPPING;
14002 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14004 ResetGfxAnimation(x, y);
14006 GfxElement[x][y] = element;
14007 GfxAction[x][y] = action;
14008 GfxDir[x][y] = direction;
14009 GfxFrame[x][y] = -1;
14014 =============================================================================
14015 checkDiagonalPushing()
14016 -----------------------------------------------------------------------------
14017 check if diagonal input device direction results in pushing of object
14018 (by checking if the alternative direction is walkable, diggable, ...)
14019 =============================================================================
14022 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14023 int x, int y, int real_dx, int real_dy)
14025 int jx, jy, dx, dy, xx, yy;
14027 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
14030 /* diagonal direction: check alternative direction */
14035 xx = jx + (dx == 0 ? real_dx : 0);
14036 yy = jy + (dy == 0 ? real_dy : 0);
14038 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14042 =============================================================================
14044 -----------------------------------------------------------------------------
14045 x, y: field next to player (non-diagonal) to try to dig to
14046 real_dx, real_dy: direction as read from input device (can be diagonal)
14047 =============================================================================
14050 static int DigField(struct PlayerInfo *player,
14051 int oldx, int oldy, int x, int y,
14052 int real_dx, int real_dy, int mode)
14054 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14055 boolean player_was_pushing = player->is_pushing;
14056 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14057 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14058 int jx = oldx, jy = oldy;
14059 int dx = x - jx, dy = y - jy;
14060 int nextx = x + dx, nexty = y + dy;
14061 int move_direction = (dx == -1 ? MV_LEFT :
14062 dx == +1 ? MV_RIGHT :
14064 dy == +1 ? MV_DOWN : MV_NONE);
14065 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14066 int dig_side = MV_DIR_OPPOSITE(move_direction);
14067 int old_element = Feld[jx][jy];
14068 #if USE_FIXED_DONT_RUN_INTO
14069 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14075 if (is_player) /* function can also be called by EL_PENGUIN */
14077 if (player->MovPos == 0)
14079 player->is_digging = FALSE;
14080 player->is_collecting = FALSE;
14083 if (player->MovPos == 0) /* last pushing move finished */
14084 player->is_pushing = FALSE;
14086 if (mode == DF_NO_PUSH) /* player just stopped pushing */
14088 player->is_switching = FALSE;
14089 player->push_delay = -1;
14091 return MP_NO_ACTION;
14095 #if !USE_FIXED_DONT_RUN_INTO
14096 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14097 return MP_NO_ACTION;
14100 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14101 old_element = Back[jx][jy];
14103 /* in case of element dropped at player position, check background */
14104 else if (Back[jx][jy] != EL_EMPTY &&
14105 game.engine_version >= VERSION_IDENT(2,2,0,0))
14106 old_element = Back[jx][jy];
14108 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14109 return MP_NO_ACTION; /* field has no opening in this direction */
14111 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14112 return MP_NO_ACTION; /* field has no opening in this direction */
14114 #if USE_FIXED_DONT_RUN_INTO
14115 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14119 Feld[jx][jy] = player->artwork_element;
14120 InitMovingField(jx, jy, MV_DOWN);
14121 Store[jx][jy] = EL_ACID;
14122 ContinueMoving(jx, jy);
14123 BuryPlayer(player);
14125 return MP_DONT_RUN_INTO;
14129 #if USE_FIXED_DONT_RUN_INTO
14130 if (player_can_move && DONT_RUN_INTO(element))
14132 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14134 return MP_DONT_RUN_INTO;
14138 #if USE_FIXED_DONT_RUN_INTO
14139 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14140 return MP_NO_ACTION;
14143 #if !USE_FIXED_DONT_RUN_INTO
14144 element = Feld[x][y];
14147 collect_count = element_info[element].collect_count_initial;
14149 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
14150 return MP_NO_ACTION;
14152 if (game.engine_version < VERSION_IDENT(2,2,0,0))
14153 player_can_move = player_can_move_or_snap;
14155 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14156 game.engine_version >= VERSION_IDENT(2,2,0,0))
14158 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14159 player->index_bit, dig_side);
14160 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14161 player->index_bit, dig_side);
14163 if (element == EL_DC_LANDMINE)
14166 if (Feld[x][y] != element) /* field changed by snapping */
14169 return MP_NO_ACTION;
14172 #if USE_PLAYER_GRAVITY
14173 if (player->gravity && is_player && !player->is_auto_moving &&
14174 canFallDown(player) && move_direction != MV_DOWN &&
14175 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14176 return MP_NO_ACTION; /* player cannot walk here due to gravity */
14178 if (game.gravity && is_player && !player->is_auto_moving &&
14179 canFallDown(player) && move_direction != MV_DOWN &&
14180 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14181 return MP_NO_ACTION; /* player cannot walk here due to gravity */
14184 if (player_can_move &&
14185 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14187 int sound_element = SND_ELEMENT(element);
14188 int sound_action = ACTION_WALKING;
14190 if (IS_RND_GATE(element))
14192 if (!player->key[RND_GATE_NR(element)])
14193 return MP_NO_ACTION;
14195 else if (IS_RND_GATE_GRAY(element))
14197 if (!player->key[RND_GATE_GRAY_NR(element)])
14198 return MP_NO_ACTION;
14200 else if (IS_RND_GATE_GRAY_ACTIVE(element))
14202 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14203 return MP_NO_ACTION;
14205 else if (element == EL_EXIT_OPEN ||
14206 element == EL_EM_EXIT_OPEN ||
14207 element == EL_STEEL_EXIT_OPEN ||
14208 element == EL_EM_STEEL_EXIT_OPEN ||
14209 element == EL_SP_EXIT_OPEN ||
14210 element == EL_SP_EXIT_OPENING)
14212 sound_action = ACTION_PASSING; /* player is passing exit */
14214 else if (element == EL_EMPTY)
14216 sound_action = ACTION_MOVING; /* nothing to walk on */
14219 /* play sound from background or player, whatever is available */
14220 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14221 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14223 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14225 else if (player_can_move &&
14226 IS_PASSABLE(element) && canPassField(x, y, move_direction))
14228 if (!ACCESS_FROM(element, opposite_direction))
14229 return MP_NO_ACTION; /* field not accessible from this direction */
14231 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
14232 return MP_NO_ACTION;
14234 if (IS_EM_GATE(element))
14236 if (!player->key[EM_GATE_NR(element)])
14237 return MP_NO_ACTION;
14239 else if (IS_EM_GATE_GRAY(element))
14241 if (!player->key[EM_GATE_GRAY_NR(element)])
14242 return MP_NO_ACTION;
14244 else if (IS_EM_GATE_GRAY_ACTIVE(element))
14246 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14247 return MP_NO_ACTION;
14249 else if (IS_EMC_GATE(element))
14251 if (!player->key[EMC_GATE_NR(element)])
14252 return MP_NO_ACTION;
14254 else if (IS_EMC_GATE_GRAY(element))
14256 if (!player->key[EMC_GATE_GRAY_NR(element)])
14257 return MP_NO_ACTION;
14259 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14261 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14262 return MP_NO_ACTION;
14264 else if (element == EL_DC_GATE_WHITE ||
14265 element == EL_DC_GATE_WHITE_GRAY ||
14266 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14268 if (player->num_white_keys == 0)
14269 return MP_NO_ACTION;
14271 player->num_white_keys--;
14273 else if (IS_SP_PORT(element))
14275 if (element == EL_SP_GRAVITY_PORT_LEFT ||
14276 element == EL_SP_GRAVITY_PORT_RIGHT ||
14277 element == EL_SP_GRAVITY_PORT_UP ||
14278 element == EL_SP_GRAVITY_PORT_DOWN)
14279 #if USE_PLAYER_GRAVITY
14280 player->gravity = !player->gravity;
14282 game.gravity = !game.gravity;
14284 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14285 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14286 element == EL_SP_GRAVITY_ON_PORT_UP ||
14287 element == EL_SP_GRAVITY_ON_PORT_DOWN)
14288 #if USE_PLAYER_GRAVITY
14289 player->gravity = TRUE;
14291 game.gravity = TRUE;
14293 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14294 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14295 element == EL_SP_GRAVITY_OFF_PORT_UP ||
14296 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14297 #if USE_PLAYER_GRAVITY
14298 player->gravity = FALSE;
14300 game.gravity = FALSE;
14304 /* automatically move to the next field with double speed */
14305 player->programmed_action = move_direction;
14307 if (player->move_delay_reset_counter == 0)
14309 player->move_delay_reset_counter = 2; /* two double speed steps */
14311 DOUBLE_PLAYER_SPEED(player);
14314 PlayLevelSoundAction(x, y, ACTION_PASSING);
14316 else if (player_can_move_or_snap && IS_DIGGABLE(element))
14320 if (mode != DF_SNAP)
14322 GfxElement[x][y] = GFX_ELEMENT(element);
14323 player->is_digging = TRUE;
14326 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14328 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14329 player->index_bit, dig_side);
14331 if (mode == DF_SNAP)
14333 #if USE_NEW_SNAP_DELAY
14334 if (level.block_snap_field)
14335 setFieldForSnapping(x, y, element, move_direction);
14337 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14339 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14342 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14343 player->index_bit, dig_side);
14346 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14350 if (is_player && mode != DF_SNAP)
14352 GfxElement[x][y] = element;
14353 player->is_collecting = TRUE;
14356 if (element == EL_SPEED_PILL)
14358 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14360 else if (element == EL_EXTRA_TIME && level.time > 0)
14362 TimeLeft += level.extra_time;
14365 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14367 DisplayGameControlValues();
14369 DrawGameValue_Time(TimeLeft);
14372 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14374 player->shield_normal_time_left += level.shield_normal_time;
14375 if (element == EL_SHIELD_DEADLY)
14376 player->shield_deadly_time_left += level.shield_deadly_time;
14378 else if (element == EL_DYNAMITE ||
14379 element == EL_EM_DYNAMITE ||
14380 element == EL_SP_DISK_RED)
14382 if (player->inventory_size < MAX_INVENTORY_SIZE)
14383 player->inventory_element[player->inventory_size++] = element;
14385 DrawGameDoorValues();
14387 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14389 player->dynabomb_count++;
14390 player->dynabombs_left++;
14392 else if (element == EL_DYNABOMB_INCREASE_SIZE)
14394 player->dynabomb_size++;
14396 else if (element == EL_DYNABOMB_INCREASE_POWER)
14398 player->dynabomb_xl = TRUE;
14400 else if (IS_KEY(element))
14402 player->key[KEY_NR(element)] = TRUE;
14404 DrawGameDoorValues();
14406 else if (element == EL_DC_KEY_WHITE)
14408 player->num_white_keys++;
14410 /* display white keys? */
14411 /* DrawGameDoorValues(); */
14413 else if (IS_ENVELOPE(element))
14415 player->show_envelope = element;
14417 else if (element == EL_EMC_LENSES)
14419 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14421 RedrawAllInvisibleElementsForLenses();
14423 else if (element == EL_EMC_MAGNIFIER)
14425 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14427 RedrawAllInvisibleElementsForMagnifier();
14429 else if (IS_DROPPABLE(element) ||
14430 IS_THROWABLE(element)) /* can be collected and dropped */
14434 if (collect_count == 0)
14435 player->inventory_infinite_element = element;
14437 for (i = 0; i < collect_count; i++)
14438 if (player->inventory_size < MAX_INVENTORY_SIZE)
14439 player->inventory_element[player->inventory_size++] = element;
14441 DrawGameDoorValues();
14443 else if (collect_count > 0)
14445 local_player->gems_still_needed -= collect_count;
14446 if (local_player->gems_still_needed < 0)
14447 local_player->gems_still_needed = 0;
14450 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
14452 DisplayGameControlValues();
14454 DrawGameValue_Emeralds(local_player->gems_still_needed);
14458 RaiseScoreElement(element);
14459 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14462 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14463 player->index_bit, dig_side);
14465 if (mode == DF_SNAP)
14467 #if USE_NEW_SNAP_DELAY
14468 if (level.block_snap_field)
14469 setFieldForSnapping(x, y, element, move_direction);
14471 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14473 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14476 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14477 player->index_bit, dig_side);
14480 else if (player_can_move_or_snap && IS_PUSHABLE(element))
14482 if (mode == DF_SNAP && element != EL_BD_ROCK)
14483 return MP_NO_ACTION;
14485 if (CAN_FALL(element) && dy)
14486 return MP_NO_ACTION;
14488 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14489 !(element == EL_SPRING && level.use_spring_bug))
14490 return MP_NO_ACTION;
14492 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14493 ((move_direction & MV_VERTICAL &&
14494 ((element_info[element].move_pattern & MV_LEFT &&
14495 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14496 (element_info[element].move_pattern & MV_RIGHT &&
14497 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14498 (move_direction & MV_HORIZONTAL &&
14499 ((element_info[element].move_pattern & MV_UP &&
14500 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14501 (element_info[element].move_pattern & MV_DOWN &&
14502 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14503 return MP_NO_ACTION;
14505 /* do not push elements already moving away faster than player */
14506 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14507 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14508 return MP_NO_ACTION;
14510 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14512 if (player->push_delay_value == -1 || !player_was_pushing)
14513 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14515 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14517 if (player->push_delay_value == -1)
14518 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14520 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14522 if (!player->is_pushing)
14523 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14526 player->is_pushing = TRUE;
14527 player->is_active = TRUE;
14529 if (!(IN_LEV_FIELD(nextx, nexty) &&
14530 (IS_FREE(nextx, nexty) ||
14531 (IS_SB_ELEMENT(element) &&
14532 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14533 (IS_CUSTOM_ELEMENT(element) &&
14534 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14535 return MP_NO_ACTION;
14537 if (IS_CUSTOM_ELEMENT(element) &&
14538 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14540 if (!DigFieldByCE(nextx, nexty, element))
14541 return MP_NO_ACTION;
14544 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14545 return MP_NO_ACTION;
14547 if (player->push_delay == -1) /* new pushing; restart delay */
14548 player->push_delay = 0;
14550 if (player->push_delay < player->push_delay_value &&
14551 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14552 element != EL_SPRING && element != EL_BALLOON)
14554 /* make sure that there is no move delay before next try to push */
14555 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14556 player->move_delay = 0;
14558 return MP_NO_ACTION;
14561 if (IS_SB_ELEMENT(element))
14563 if (element == EL_SOKOBAN_FIELD_FULL)
14565 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14566 local_player->sokobanfields_still_needed++;
14569 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14571 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14572 local_player->sokobanfields_still_needed--;
14575 Feld[x][y] = EL_SOKOBAN_OBJECT;
14577 if (Back[x][y] == Back[nextx][nexty])
14578 PlayLevelSoundAction(x, y, ACTION_PUSHING);
14579 else if (Back[x][y] != 0)
14580 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14583 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14586 if (local_player->sokobanfields_still_needed == 0 &&
14587 game.emulation == EMU_SOKOBAN)
14589 PlayerWins(player);
14591 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14595 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14597 InitMovingField(x, y, move_direction);
14598 GfxAction[x][y] = ACTION_PUSHING;
14600 if (mode == DF_SNAP)
14601 ContinueMoving(x, y);
14603 MovPos[x][y] = (dx != 0 ? dx : dy);
14605 Pushed[x][y] = TRUE;
14606 Pushed[nextx][nexty] = TRUE;
14608 if (game.engine_version < VERSION_IDENT(2,2,0,7))
14609 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14611 player->push_delay_value = -1; /* get new value later */
14613 /* check for element change _after_ element has been pushed */
14614 if (game.use_change_when_pushing_bug)
14616 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14617 player->index_bit, dig_side);
14618 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14619 player->index_bit, dig_side);
14622 else if (IS_SWITCHABLE(element))
14624 if (PLAYER_SWITCHING(player, x, y))
14626 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14627 player->index_bit, dig_side);
14632 player->is_switching = TRUE;
14633 player->switch_x = x;
14634 player->switch_y = y;
14636 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14638 if (element == EL_ROBOT_WHEEL)
14640 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14644 game.robot_wheel_active = TRUE;
14646 DrawLevelField(x, y);
14648 else if (element == EL_SP_TERMINAL)
14652 SCAN_PLAYFIELD(xx, yy)
14654 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14656 else if (Feld[xx][yy] == EL_SP_TERMINAL)
14657 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14660 else if (IS_BELT_SWITCH(element))
14662 ToggleBeltSwitch(x, y);
14664 else if (element == EL_SWITCHGATE_SWITCH_UP ||
14665 element == EL_SWITCHGATE_SWITCH_DOWN ||
14666 element == EL_DC_SWITCHGATE_SWITCH_UP ||
14667 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14669 ToggleSwitchgateSwitch(x, y);
14671 else if (element == EL_LIGHT_SWITCH ||
14672 element == EL_LIGHT_SWITCH_ACTIVE)
14674 ToggleLightSwitch(x, y);
14676 else if (element == EL_TIMEGATE_SWITCH ||
14677 element == EL_DC_TIMEGATE_SWITCH)
14679 ActivateTimegateSwitch(x, y);
14681 else if (element == EL_BALLOON_SWITCH_LEFT ||
14682 element == EL_BALLOON_SWITCH_RIGHT ||
14683 element == EL_BALLOON_SWITCH_UP ||
14684 element == EL_BALLOON_SWITCH_DOWN ||
14685 element == EL_BALLOON_SWITCH_NONE ||
14686 element == EL_BALLOON_SWITCH_ANY)
14688 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
14689 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14690 element == EL_BALLOON_SWITCH_UP ? MV_UP :
14691 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
14692 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
14695 else if (element == EL_LAMP)
14697 Feld[x][y] = EL_LAMP_ACTIVE;
14698 local_player->lights_still_needed--;
14700 ResetGfxAnimation(x, y);
14701 DrawLevelField(x, y);
14703 else if (element == EL_TIME_ORB_FULL)
14705 Feld[x][y] = EL_TIME_ORB_EMPTY;
14707 if (level.time > 0 || level.use_time_orb_bug)
14709 TimeLeft += level.time_orb_time;
14712 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14714 DisplayGameControlValues();
14716 DrawGameValue_Time(TimeLeft);
14720 ResetGfxAnimation(x, y);
14721 DrawLevelField(x, y);
14723 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14724 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14728 game.ball_state = !game.ball_state;
14730 SCAN_PLAYFIELD(xx, yy)
14732 int e = Feld[xx][yy];
14734 if (game.ball_state)
14736 if (e == EL_EMC_MAGIC_BALL)
14737 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14738 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14739 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14743 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14744 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14745 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14746 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14751 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14752 player->index_bit, dig_side);
14754 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14755 player->index_bit, dig_side);
14757 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14758 player->index_bit, dig_side);
14764 if (!PLAYER_SWITCHING(player, x, y))
14766 player->is_switching = TRUE;
14767 player->switch_x = x;
14768 player->switch_y = y;
14770 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14771 player->index_bit, dig_side);
14772 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14773 player->index_bit, dig_side);
14775 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14776 player->index_bit, dig_side);
14777 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14778 player->index_bit, dig_side);
14781 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14782 player->index_bit, dig_side);
14783 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14784 player->index_bit, dig_side);
14786 return MP_NO_ACTION;
14789 player->push_delay = -1;
14791 if (is_player) /* function can also be called by EL_PENGUIN */
14793 if (Feld[x][y] != element) /* really digged/collected something */
14795 player->is_collecting = !player->is_digging;
14796 player->is_active = TRUE;
14803 static boolean DigFieldByCE(int x, int y, int digging_element)
14805 int element = Feld[x][y];
14807 if (!IS_FREE(x, y))
14809 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14810 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14813 /* no element can dig solid indestructible elements */
14814 if (IS_INDESTRUCTIBLE(element) &&
14815 !IS_DIGGABLE(element) &&
14816 !IS_COLLECTIBLE(element))
14819 if (AmoebaNr[x][y] &&
14820 (element == EL_AMOEBA_FULL ||
14821 element == EL_BD_AMOEBA ||
14822 element == EL_AMOEBA_GROWING))
14824 AmoebaCnt[AmoebaNr[x][y]]--;
14825 AmoebaCnt2[AmoebaNr[x][y]]--;
14828 if (IS_MOVING(x, y))
14829 RemoveMovingField(x, y);
14833 DrawLevelField(x, y);
14836 /* if digged element was about to explode, prevent the explosion */
14837 ExplodeField[x][y] = EX_TYPE_NONE;
14839 PlayLevelSoundAction(x, y, action);
14842 Store[x][y] = EL_EMPTY;
14845 /* this makes it possible to leave the removed element again */
14846 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14847 Store[x][y] = element;
14849 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14851 int move_leave_element = element_info[digging_element].move_leave_element;
14853 /* this makes it possible to leave the removed element again */
14854 Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
14855 element : move_leave_element);
14862 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14864 int jx = player->jx, jy = player->jy;
14865 int x = jx + dx, y = jy + dy;
14866 int snap_direction = (dx == -1 ? MV_LEFT :
14867 dx == +1 ? MV_RIGHT :
14869 dy == +1 ? MV_DOWN : MV_NONE);
14870 boolean can_continue_snapping = (level.continuous_snapping &&
14871 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14873 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14876 if (!player->active || !IN_LEV_FIELD(x, y))
14884 if (player->MovPos == 0)
14885 player->is_pushing = FALSE;
14887 player->is_snapping = FALSE;
14889 if (player->MovPos == 0)
14891 player->is_moving = FALSE;
14892 player->is_digging = FALSE;
14893 player->is_collecting = FALSE;
14899 #if USE_NEW_CONTINUOUS_SNAPPING
14900 /* prevent snapping with already pressed snap key when not allowed */
14901 if (player->is_snapping && !can_continue_snapping)
14904 if (player->is_snapping)
14908 player->MovDir = snap_direction;
14910 if (player->MovPos == 0)
14912 player->is_moving = FALSE;
14913 player->is_digging = FALSE;
14914 player->is_collecting = FALSE;
14917 player->is_dropping = FALSE;
14918 player->is_dropping_pressed = FALSE;
14919 player->drop_pressed_delay = 0;
14921 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14924 player->is_snapping = TRUE;
14925 player->is_active = TRUE;
14927 if (player->MovPos == 0)
14929 player->is_moving = FALSE;
14930 player->is_digging = FALSE;
14931 player->is_collecting = FALSE;
14934 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
14935 DrawLevelField(player->last_jx, player->last_jy);
14937 DrawLevelField(x, y);
14942 static boolean DropElement(struct PlayerInfo *player)
14944 int old_element, new_element;
14945 int dropx = player->jx, dropy = player->jy;
14946 int drop_direction = player->MovDir;
14947 int drop_side = drop_direction;
14949 int drop_element = get_next_dropped_element(player);
14951 int drop_element = (player->inventory_size > 0 ?
14952 player->inventory_element[player->inventory_size - 1] :
14953 player->inventory_infinite_element != EL_UNDEFINED ?
14954 player->inventory_infinite_element :
14955 player->dynabombs_left > 0 ?
14956 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
14960 player->is_dropping_pressed = TRUE;
14962 /* do not drop an element on top of another element; when holding drop key
14963 pressed without moving, dropped element must move away before the next
14964 element can be dropped (this is especially important if the next element
14965 is dynamite, which can be placed on background for historical reasons) */
14966 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14969 if (IS_THROWABLE(drop_element))
14971 dropx += GET_DX_FROM_DIR(drop_direction);
14972 dropy += GET_DY_FROM_DIR(drop_direction);
14974 if (!IN_LEV_FIELD(dropx, dropy))
14978 old_element = Feld[dropx][dropy]; /* old element at dropping position */
14979 new_element = drop_element; /* default: no change when dropping */
14981 /* check if player is active, not moving and ready to drop */
14982 if (!player->active || player->MovPos || player->drop_delay > 0)
14985 /* check if player has anything that can be dropped */
14986 if (new_element == EL_UNDEFINED)
14989 /* check if drop key was pressed long enough for EM style dynamite */
14990 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14993 /* check if anything can be dropped at the current position */
14994 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14997 /* collected custom elements can only be dropped on empty fields */
14998 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15001 if (old_element != EL_EMPTY)
15002 Back[dropx][dropy] = old_element; /* store old element on this field */
15004 ResetGfxAnimation(dropx, dropy);
15005 ResetRandomAnimationValue(dropx, dropy);
15007 if (player->inventory_size > 0 ||
15008 player->inventory_infinite_element != EL_UNDEFINED)
15010 if (player->inventory_size > 0)
15012 player->inventory_size--;
15014 DrawGameDoorValues();
15016 if (new_element == EL_DYNAMITE)
15017 new_element = EL_DYNAMITE_ACTIVE;
15018 else if (new_element == EL_EM_DYNAMITE)
15019 new_element = EL_EM_DYNAMITE_ACTIVE;
15020 else if (new_element == EL_SP_DISK_RED)
15021 new_element = EL_SP_DISK_RED_ACTIVE;
15024 Feld[dropx][dropy] = new_element;
15026 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15027 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15028 el2img(Feld[dropx][dropy]), 0);
15030 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15032 /* needed if previous element just changed to "empty" in the last frame */
15033 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15035 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15036 player->index_bit, drop_side);
15037 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15039 player->index_bit, drop_side);
15041 TestIfElementTouchesCustomElement(dropx, dropy);
15043 else /* player is dropping a dyna bomb */
15045 player->dynabombs_left--;
15047 Feld[dropx][dropy] = new_element;
15049 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15050 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15051 el2img(Feld[dropx][dropy]), 0);
15053 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15056 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15057 InitField_WithBug1(dropx, dropy, FALSE);
15059 new_element = Feld[dropx][dropy]; /* element might have changed */
15061 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15062 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15064 int move_direction, nextx, nexty;
15066 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15067 MovDir[dropx][dropy] = drop_direction;
15069 move_direction = MovDir[dropx][dropy];
15070 nextx = dropx + GET_DX_FROM_DIR(move_direction);
15071 nexty = dropy + GET_DY_FROM_DIR(move_direction);
15073 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15075 #if USE_FIX_IMPACT_COLLISION
15076 /* do not cause impact style collision by dropping elements that can fall */
15077 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15079 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15083 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15084 player->is_dropping = TRUE;
15086 player->drop_pressed_delay = 0;
15087 player->is_dropping_pressed = FALSE;
15089 player->drop_x = dropx;
15090 player->drop_y = dropy;
15095 /* ------------------------------------------------------------------------- */
15096 /* game sound playing functions */
15097 /* ------------------------------------------------------------------------- */
15099 static int *loop_sound_frame = NULL;
15100 static int *loop_sound_volume = NULL;
15102 void InitPlayLevelSound()
15104 int num_sounds = getSoundListSize();
15106 checked_free(loop_sound_frame);
15107 checked_free(loop_sound_volume);
15109 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
15110 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15113 static void PlayLevelSound(int x, int y, int nr)
15115 int sx = SCREENX(x), sy = SCREENY(y);
15116 int volume, stereo_position;
15117 int max_distance = 8;
15118 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15120 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15121 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15124 if (!IN_LEV_FIELD(x, y) ||
15125 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15126 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15129 volume = SOUND_MAX_VOLUME;
15131 if (!IN_SCR_FIELD(sx, sy))
15133 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15134 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15136 volume -= volume * (dx > dy ? dx : dy) / max_distance;
15139 stereo_position = (SOUND_MAX_LEFT +
15140 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15141 (SCR_FIELDX + 2 * max_distance));
15143 if (IS_LOOP_SOUND(nr))
15145 /* This assures that quieter loop sounds do not overwrite louder ones,
15146 while restarting sound volume comparison with each new game frame. */
15148 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15151 loop_sound_volume[nr] = volume;
15152 loop_sound_frame[nr] = FrameCounter;
15155 PlaySoundExt(nr, volume, stereo_position, type);
15158 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15160 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15161 x > LEVELX(BX2) ? LEVELX(BX2) : x,
15162 y < LEVELY(BY1) ? LEVELY(BY1) :
15163 y > LEVELY(BY2) ? LEVELY(BY2) : y,
15167 static void PlayLevelSoundAction(int x, int y, int action)
15169 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
15172 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15174 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15176 if (sound_effect != SND_UNDEFINED)
15177 PlayLevelSound(x, y, sound_effect);
15180 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15183 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15185 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15186 PlayLevelSound(x, y, sound_effect);
15189 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15191 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15193 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15194 PlayLevelSound(x, y, sound_effect);
15197 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15199 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15201 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15202 StopSound(sound_effect);
15205 static void PlayLevelMusic()
15207 if (levelset.music[level_nr] != MUS_UNDEFINED)
15208 PlayMusic(levelset.music[level_nr]); /* from config file */
15210 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
15213 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15215 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
15216 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
15217 int x = xx - 1 - offset;
15218 int y = yy - 1 - offset;
15223 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15227 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15231 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15235 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15239 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15243 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15247 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15250 case SAMPLE_android_clone:
15251 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15254 case SAMPLE_android_move:
15255 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15258 case SAMPLE_spring:
15259 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15263 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15267 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15270 case SAMPLE_eater_eat:
15271 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15275 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15278 case SAMPLE_collect:
15279 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15282 case SAMPLE_diamond:
15283 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15286 case SAMPLE_squash:
15287 /* !!! CHECK THIS !!! */
15289 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15291 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15295 case SAMPLE_wonderfall:
15296 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15300 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15304 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15308 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15312 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15316 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15320 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15323 case SAMPLE_wonder:
15324 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15328 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15331 case SAMPLE_exit_open:
15332 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15335 case SAMPLE_exit_leave:
15336 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15339 case SAMPLE_dynamite:
15340 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15344 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15348 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15352 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15356 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15360 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15364 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15368 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15374 void ChangeTime(int value)
15376 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
15380 /* EMC game engine uses value from time counter of RND game engine */
15381 level.native_em_level->lev->time = *time;
15383 DrawGameValue_Time(*time);
15386 void RaiseScore(int value)
15388 /* EMC game engine and RND game engine have separate score counters */
15389 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
15390 &level.native_em_level->lev->score : &local_player->score);
15394 DrawGameValue_Score(*score);
15398 void RaiseScore(int value)
15400 local_player->score += value;
15403 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
15405 DisplayGameControlValues();
15407 DrawGameValue_Score(local_player->score);
15411 void RaiseScoreElement(int element)
15416 case EL_BD_DIAMOND:
15417 case EL_EMERALD_YELLOW:
15418 case EL_EMERALD_RED:
15419 case EL_EMERALD_PURPLE:
15420 case EL_SP_INFOTRON:
15421 RaiseScore(level.score[SC_EMERALD]);
15424 RaiseScore(level.score[SC_DIAMOND]);
15427 RaiseScore(level.score[SC_CRYSTAL]);
15430 RaiseScore(level.score[SC_PEARL]);
15433 case EL_BD_BUTTERFLY:
15434 case EL_SP_ELECTRON:
15435 RaiseScore(level.score[SC_BUG]);
15438 case EL_BD_FIREFLY:
15439 case EL_SP_SNIKSNAK:
15440 RaiseScore(level.score[SC_SPACESHIP]);
15443 case EL_DARK_YAMYAM:
15444 RaiseScore(level.score[SC_YAMYAM]);
15447 RaiseScore(level.score[SC_ROBOT]);
15450 RaiseScore(level.score[SC_PACMAN]);
15453 RaiseScore(level.score[SC_NUT]);
15456 case EL_EM_DYNAMITE:
15457 case EL_SP_DISK_RED:
15458 case EL_DYNABOMB_INCREASE_NUMBER:
15459 case EL_DYNABOMB_INCREASE_SIZE:
15460 case EL_DYNABOMB_INCREASE_POWER:
15461 RaiseScore(level.score[SC_DYNAMITE]);
15463 case EL_SHIELD_NORMAL:
15464 case EL_SHIELD_DEADLY:
15465 RaiseScore(level.score[SC_SHIELD]);
15467 case EL_EXTRA_TIME:
15468 RaiseScore(level.extra_time_score);
15482 case EL_DC_KEY_WHITE:
15483 RaiseScore(level.score[SC_KEY]);
15486 RaiseScore(element_info[element].collect_score);
15491 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15493 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15495 #if defined(NETWORK_AVALIABLE)
15496 if (options.network)
15497 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15506 FadeSkipNextFadeIn();
15508 fading = fading_none;
15512 OpenDoor(DOOR_CLOSE_1);
15515 game_status = GAME_MODE_MAIN;
15518 DrawAndFadeInMainMenu(REDRAW_FIELD);
15526 FadeOut(REDRAW_FIELD);
15529 game_status = GAME_MODE_MAIN;
15531 DrawAndFadeInMainMenu(REDRAW_FIELD);
15535 else /* continue playing the game */
15537 if (tape.playing && tape.deactivate_display)
15538 TapeDeactivateDisplayOff(TRUE);
15540 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15542 if (tape.playing && tape.deactivate_display)
15543 TapeDeactivateDisplayOn();
15547 void RequestQuitGame(boolean ask_if_really_quit)
15549 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15550 boolean skip_request = AllPlayersGone || quick_quit;
15552 RequestQuitGameExt(skip_request, quick_quit,
15553 "Do you really want to quit the game ?");
15557 /* ------------------------------------------------------------------------- */
15558 /* random generator functions */
15559 /* ------------------------------------------------------------------------- */
15561 unsigned int InitEngineRandom_RND(long seed)
15563 game.num_random_calls = 0;
15566 unsigned int rnd_seed = InitEngineRandom(seed);
15568 printf("::: START RND: %d\n", rnd_seed);
15573 return InitEngineRandom(seed);
15579 unsigned int RND(int max)
15583 game.num_random_calls++;
15585 return GetEngineRandom(max);
15592 /* ------------------------------------------------------------------------- */
15593 /* game engine snapshot handling functions */
15594 /* ------------------------------------------------------------------------- */
15596 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
15598 struct EngineSnapshotInfo
15600 /* runtime values for custom element collect score */
15601 int collect_score[NUM_CUSTOM_ELEMENTS];
15603 /* runtime values for group element choice position */
15604 int choice_pos[NUM_GROUP_ELEMENTS];
15606 /* runtime values for belt position animations */
15607 int belt_graphic[4 * NUM_BELT_PARTS];
15608 int belt_anim_mode[4 * NUM_BELT_PARTS];
15611 struct EngineSnapshotNodeInfo
15618 static struct EngineSnapshotInfo engine_snapshot_rnd;
15619 static ListNode *engine_snapshot_list = NULL;
15620 static char *snapshot_level_identifier = NULL;
15621 static int snapshot_level_nr = -1;
15623 void FreeEngineSnapshot()
15625 while (engine_snapshot_list != NULL)
15626 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
15629 setString(&snapshot_level_identifier, NULL);
15630 snapshot_level_nr = -1;
15633 static void SaveEngineSnapshotValues_RND()
15635 static int belt_base_active_element[4] =
15637 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15638 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15639 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15640 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15644 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15646 int element = EL_CUSTOM_START + i;
15648 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15651 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15653 int element = EL_GROUP_START + i;
15655 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15658 for (i = 0; i < 4; i++)
15660 for (j = 0; j < NUM_BELT_PARTS; j++)
15662 int element = belt_base_active_element[i] + j;
15663 int graphic = el2img(element);
15664 int anim_mode = graphic_info[graphic].anim_mode;
15666 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
15667 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
15672 static void LoadEngineSnapshotValues_RND()
15674 unsigned long num_random_calls = game.num_random_calls;
15677 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15679 int element = EL_CUSTOM_START + i;
15681 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15684 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15686 int element = EL_GROUP_START + i;
15688 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15691 for (i = 0; i < 4; i++)
15693 for (j = 0; j < NUM_BELT_PARTS; j++)
15695 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
15696 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
15698 graphic_info[graphic].anim_mode = anim_mode;
15702 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15704 InitRND(tape.random_seed);
15705 for (i = 0; i < num_random_calls; i++)
15709 if (game.num_random_calls != num_random_calls)
15711 Error(ERR_INFO, "number of random calls out of sync");
15712 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15713 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15714 Error(ERR_EXIT, "this should not happen -- please debug");
15718 static void SaveEngineSnapshotBuffer(void *buffer, int size)
15720 struct EngineSnapshotNodeInfo *bi =
15721 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
15723 bi->buffer_orig = buffer;
15724 bi->buffer_copy = checked_malloc(size);
15727 memcpy(bi->buffer_copy, buffer, size);
15729 addNodeToList(&engine_snapshot_list, NULL, bi);
15732 void SaveEngineSnapshot()
15734 FreeEngineSnapshot(); /* free previous snapshot, if needed */
15736 if (level_editor_test_game) /* do not save snapshots from editor */
15739 /* copy some special values to a structure better suited for the snapshot */
15741 SaveEngineSnapshotValues_RND();
15742 SaveEngineSnapshotValues_EM();
15744 /* save values stored in special snapshot structure */
15746 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15747 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15749 /* save further RND engine values */
15751 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
15752 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
15753 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
15755 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
15756 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
15757 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
15758 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
15760 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15761 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15762 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15763 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15764 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15766 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15767 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15768 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15770 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15772 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15774 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15775 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15777 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
15778 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
15779 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
15780 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15781 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15782 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15783 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15784 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
15785 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
15786 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15787 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
15788 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15789 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15790 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15791 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15792 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15793 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
15794 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
15796 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15797 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15799 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15800 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15801 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15803 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15804 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15806 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15807 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15808 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15809 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15810 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15812 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15813 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15815 /* save level identification information */
15817 setString(&snapshot_level_identifier, leveldir_current->identifier);
15818 snapshot_level_nr = level_nr;
15821 ListNode *node = engine_snapshot_list;
15824 while (node != NULL)
15826 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15831 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15835 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
15837 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
15840 void LoadEngineSnapshot()
15842 ListNode *node = engine_snapshot_list;
15844 if (engine_snapshot_list == NULL)
15847 while (node != NULL)
15849 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
15854 /* restore special values from snapshot structure */
15856 LoadEngineSnapshotValues_RND();
15857 LoadEngineSnapshotValues_EM();
15860 boolean CheckEngineSnapshot()
15862 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15863 snapshot_level_nr == level_nr);
15867 /* ---------- new game button stuff ---------------------------------------- */
15869 /* graphic position values for game buttons */
15870 #define GAME_BUTTON_XSIZE 30
15871 #define GAME_BUTTON_YSIZE 30
15872 #define GAME_BUTTON_XPOS 5
15873 #define GAME_BUTTON_YPOS 215
15874 #define SOUND_BUTTON_XPOS 5
15875 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
15877 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15878 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15879 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15880 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15881 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15882 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15890 } gamebutton_info[NUM_GAME_BUTTONS] =
15894 &game.button.stop.x, &game.button.stop.y,
15895 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
15900 &game.button.pause.x, &game.button.pause.y,
15901 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
15902 GAME_CTRL_ID_PAUSE,
15906 &game.button.play.x, &game.button.play.y,
15907 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
15912 &game.button.sound_music.x, &game.button.sound_music.y,
15913 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
15914 SOUND_CTRL_ID_MUSIC,
15915 "background music on/off"
15918 &game.button.sound_loops.x, &game.button.sound_loops.y,
15919 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
15920 SOUND_CTRL_ID_LOOPS,
15921 "sound loops on/off"
15924 &game.button.sound_simple.x,&game.button.sound_simple.y,
15925 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
15926 SOUND_CTRL_ID_SIMPLE,
15927 "normal sounds on/off"
15931 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
15936 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
15937 GAME_CTRL_ID_PAUSE,
15941 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
15946 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
15947 SOUND_CTRL_ID_MUSIC,
15948 "background music on/off"
15951 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
15952 SOUND_CTRL_ID_LOOPS,
15953 "sound loops on/off"
15956 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
15957 SOUND_CTRL_ID_SIMPLE,
15958 "normal sounds on/off"
15963 void CreateGameButtons()
15967 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15969 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
15970 struct GadgetInfo *gi;
15973 unsigned long event_mask;
15975 int gd_xoffset, gd_yoffset;
15976 int gd_x1, gd_x2, gd_y1, gd_y2;
15979 x = DX + *gamebutton_info[i].x;
15980 y = DY + *gamebutton_info[i].y;
15981 gd_xoffset = gamebutton_info[i].gd_x;
15982 gd_yoffset = gamebutton_info[i].gd_y;
15983 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
15984 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
15986 if (id == GAME_CTRL_ID_STOP ||
15987 id == GAME_CTRL_ID_PAUSE ||
15988 id == GAME_CTRL_ID_PLAY)
15990 button_type = GD_TYPE_NORMAL_BUTTON;
15992 event_mask = GD_EVENT_RELEASED;
15993 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15994 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15998 button_type = GD_TYPE_CHECK_BUTTON;
16000 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16001 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16002 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16003 event_mask = GD_EVENT_PRESSED;
16004 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
16005 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16008 gi = CreateGadget(GDI_CUSTOM_ID, id,
16009 GDI_INFO_TEXT, gamebutton_info[i].infotext,
16014 GDI_X, DX + gd_xoffset,
16015 GDI_Y, DY + gd_yoffset,
16017 GDI_WIDTH, GAME_BUTTON_XSIZE,
16018 GDI_HEIGHT, GAME_BUTTON_YSIZE,
16019 GDI_TYPE, button_type,
16020 GDI_STATE, GD_BUTTON_UNPRESSED,
16021 GDI_CHECKED, checked,
16022 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
16023 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
16024 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
16025 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
16026 GDI_DIRECT_DRAW, FALSE,
16027 GDI_EVENT_MASK, event_mask,
16028 GDI_CALLBACK_ACTION, HandleGameButtons,
16032 Error(ERR_EXIT, "cannot create gadget");
16034 game_gadget[id] = gi;
16038 void FreeGameButtons()
16042 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16043 FreeGadget(game_gadget[i]);
16046 static void MapGameButtons()
16050 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16051 MapGadget(game_gadget[i]);
16054 void UnmapGameButtons()
16058 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16059 UnmapGadget(game_gadget[i]);
16062 void RedrawGameButtons()
16066 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16067 RedrawGadget(game_gadget[i]);
16070 static void HandleGameButtons(struct GadgetInfo *gi)
16072 int id = gi->custom_id;
16074 if (game_status != GAME_MODE_PLAYING)
16079 case GAME_CTRL_ID_STOP:
16083 RequestQuitGame(TRUE);
16086 case GAME_CTRL_ID_PAUSE:
16087 if (options.network)
16089 #if defined(NETWORK_AVALIABLE)
16091 SendToServer_ContinuePlaying();
16093 SendToServer_PausePlaying();
16097 TapeTogglePause(TAPE_TOGGLE_MANUAL);
16100 case GAME_CTRL_ID_PLAY:
16103 #if defined(NETWORK_AVALIABLE)
16104 if (options.network)
16105 SendToServer_ContinuePlaying();
16109 tape.pausing = FALSE;
16110 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16115 case SOUND_CTRL_ID_MUSIC:
16116 if (setup.sound_music)
16118 setup.sound_music = FALSE;
16121 else if (audio.music_available)
16123 setup.sound = setup.sound_music = TRUE;
16125 SetAudioMode(setup.sound);
16131 case SOUND_CTRL_ID_LOOPS:
16132 if (setup.sound_loops)
16133 setup.sound_loops = FALSE;
16134 else if (audio.loops_available)
16136 setup.sound = setup.sound_loops = TRUE;
16137 SetAudioMode(setup.sound);
16141 case SOUND_CTRL_ID_SIMPLE:
16142 if (setup.sound_simple)
16143 setup.sound_simple = FALSE;
16144 else if (audio.sound_available)
16146 setup.sound = setup.sound_simple = TRUE;
16147 SetAudioMode(setup.sound);