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_TIME 30
165 #define GAME_PANEL_TIME_HH 31
166 #define GAME_PANEL_TIME_MM 32
167 #define GAME_PANEL_TIME_SS 33
168 #define GAME_PANEL_SHIELD_NORMAL 34
169 #define GAME_PANEL_SHIELD_NORMAL_TIME 35
170 #define GAME_PANEL_SHIELD_DEADLY 36
171 #define GAME_PANEL_SHIELD_DEADLY_TIME 37
172 #define GAME_PANEL_EXIT 38
173 #define GAME_PANEL_EMC_MAGIC_BALL 39
174 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH 40
175 #define GAME_PANEL_LIGHT_SWITCH 41
176 #define GAME_PANEL_LIGHT_SWITCH_TIME 42
177 #define GAME_PANEL_TIMEGATE_SWITCH 43
178 #define GAME_PANEL_TIMEGATE_SWITCH_TIME 44
179 #define GAME_PANEL_SWITCHGATE_SWITCH 45
180 #define GAME_PANEL_EMC_LENSES 46
181 #define GAME_PANEL_EMC_LENSES_TIME 47
182 #define GAME_PANEL_EMC_MAGNIFIER 48
183 #define GAME_PANEL_EMC_MAGNIFIER_TIME 49
184 #define GAME_PANEL_BALLOON_SWITCH 50
185 #define GAME_PANEL_DYNABOMB_NUMBER 51
186 #define GAME_PANEL_DYNABOMB_SIZE 52
187 #define GAME_PANEL_DYNABOMB_POWER 53
188 #define GAME_PANEL_PENGUINS 54
189 #define GAME_PANEL_SOKOBAN_OBJECTS 55
190 #define GAME_PANEL_SOKOBAN_FIELDS 56
191 #define GAME_PANEL_ROBOT_WHEEL 57
192 #define GAME_PANEL_CONVEYOR_BELT_1 58
193 #define GAME_PANEL_CONVEYOR_BELT_2 59
194 #define GAME_PANEL_CONVEYOR_BELT_3 60
195 #define GAME_PANEL_CONVEYOR_BELT_4 61
196 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH 62
197 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH 63
198 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH 64
199 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH 65
200 #define GAME_PANEL_MAGIC_WALL 66
201 #define GAME_PANEL_MAGIC_WALL_TIME 67
202 #define GAME_PANEL_GRAVITY_STATE 68
203 #define GAME_PANEL_GRAPHIC_1 69
204 #define GAME_PANEL_GRAPHIC_2 70
205 #define GAME_PANEL_GRAPHIC_3 71
206 #define GAME_PANEL_GRAPHIC_4 72
207 #define GAME_PANEL_GRAPHIC_5 73
208 #define GAME_PANEL_GRAPHIC_6 74
209 #define GAME_PANEL_GRAPHIC_7 75
210 #define GAME_PANEL_GRAPHIC_8 76
211 #define GAME_PANEL_ELEMENT_1 77
212 #define GAME_PANEL_ELEMENT_2 78
213 #define GAME_PANEL_ELEMENT_3 79
214 #define GAME_PANEL_ELEMENT_4 80
215 #define GAME_PANEL_ELEMENT_5 81
216 #define GAME_PANEL_ELEMENT_6 82
217 #define GAME_PANEL_ELEMENT_7 83
218 #define GAME_PANEL_ELEMENT_8 84
219 #define GAME_PANEL_ELEMENT_COUNT_1 85
220 #define GAME_PANEL_ELEMENT_COUNT_2 86
221 #define GAME_PANEL_ELEMENT_COUNT_3 87
222 #define GAME_PANEL_ELEMENT_COUNT_4 88
223 #define GAME_PANEL_ELEMENT_COUNT_5 89
224 #define GAME_PANEL_ELEMENT_COUNT_6 90
225 #define GAME_PANEL_ELEMENT_COUNT_7 91
226 #define GAME_PANEL_ELEMENT_COUNT_8 92
227 #define GAME_PANEL_CE_SCORE_1 93
228 #define GAME_PANEL_CE_SCORE_2 94
229 #define GAME_PANEL_CE_SCORE_3 95
230 #define GAME_PANEL_CE_SCORE_4 96
231 #define GAME_PANEL_CE_SCORE_5 97
232 #define GAME_PANEL_CE_SCORE_6 98
233 #define GAME_PANEL_CE_SCORE_7 99
234 #define GAME_PANEL_CE_SCORE_8 100
235 #define GAME_PANEL_CE_SCORE_1_ELEMENT 101
236 #define GAME_PANEL_CE_SCORE_2_ELEMENT 102
237 #define GAME_PANEL_CE_SCORE_3_ELEMENT 103
238 #define GAME_PANEL_CE_SCORE_4_ELEMENT 104
239 #define GAME_PANEL_CE_SCORE_5_ELEMENT 105
240 #define GAME_PANEL_CE_SCORE_6_ELEMENT 106
241 #define GAME_PANEL_CE_SCORE_7_ELEMENT 107
242 #define GAME_PANEL_CE_SCORE_8_ELEMENT 108
243 #define GAME_PANEL_PLAYER_NAME 109
244 #define GAME_PANEL_LEVEL_NAME 110
245 #define GAME_PANEL_LEVEL_AUTHOR 111
247 #define NUM_GAME_PANEL_CONTROLS 112
249 struct GamePanelOrderInfo
255 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
257 struct GamePanelControlInfo
261 struct TextPosInfo *pos;
264 int value, last_value;
265 int frame, last_frame;
270 static struct GamePanelControlInfo game_panel_controls[] =
273 GAME_PANEL_LEVEL_NUMBER,
274 &game.panel.level_number,
283 GAME_PANEL_INVENTORY_COUNT,
284 &game.panel.inventory_count,
288 GAME_PANEL_INVENTORY_FIRST_1,
289 &game.panel.inventory_first[0],
293 GAME_PANEL_INVENTORY_FIRST_2,
294 &game.panel.inventory_first[1],
298 GAME_PANEL_INVENTORY_FIRST_3,
299 &game.panel.inventory_first[2],
303 GAME_PANEL_INVENTORY_FIRST_4,
304 &game.panel.inventory_first[3],
308 GAME_PANEL_INVENTORY_FIRST_5,
309 &game.panel.inventory_first[4],
313 GAME_PANEL_INVENTORY_FIRST_6,
314 &game.panel.inventory_first[5],
318 GAME_PANEL_INVENTORY_FIRST_7,
319 &game.panel.inventory_first[6],
323 GAME_PANEL_INVENTORY_FIRST_8,
324 &game.panel.inventory_first[7],
328 GAME_PANEL_INVENTORY_LAST_1,
329 &game.panel.inventory_last[0],
333 GAME_PANEL_INVENTORY_LAST_2,
334 &game.panel.inventory_last[1],
338 GAME_PANEL_INVENTORY_LAST_3,
339 &game.panel.inventory_last[2],
343 GAME_PANEL_INVENTORY_LAST_4,
344 &game.panel.inventory_last[3],
348 GAME_PANEL_INVENTORY_LAST_5,
349 &game.panel.inventory_last[4],
353 GAME_PANEL_INVENTORY_LAST_6,
354 &game.panel.inventory_last[5],
358 GAME_PANEL_INVENTORY_LAST_7,
359 &game.panel.inventory_last[6],
363 GAME_PANEL_INVENTORY_LAST_8,
364 &game.panel.inventory_last[7],
408 GAME_PANEL_KEY_WHITE,
409 &game.panel.key_white,
413 GAME_PANEL_KEY_WHITE_COUNT,
414 &game.panel.key_white_count,
443 GAME_PANEL_SHIELD_NORMAL,
444 &game.panel.shield_normal,
448 GAME_PANEL_SHIELD_NORMAL_TIME,
449 &game.panel.shield_normal_time,
453 GAME_PANEL_SHIELD_DEADLY,
454 &game.panel.shield_deadly,
458 GAME_PANEL_SHIELD_DEADLY_TIME,
459 &game.panel.shield_deadly_time,
468 GAME_PANEL_EMC_MAGIC_BALL,
469 &game.panel.emc_magic_ball,
473 GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
474 &game.panel.emc_magic_ball_switch,
478 GAME_PANEL_LIGHT_SWITCH,
479 &game.panel.light_switch,
483 GAME_PANEL_LIGHT_SWITCH_TIME,
484 &game.panel.light_switch_time,
488 GAME_PANEL_TIMEGATE_SWITCH,
489 &game.panel.timegate_switch,
493 GAME_PANEL_TIMEGATE_SWITCH_TIME,
494 &game.panel.timegate_switch_time,
498 GAME_PANEL_SWITCHGATE_SWITCH,
499 &game.panel.switchgate_switch,
503 GAME_PANEL_EMC_LENSES,
504 &game.panel.emc_lenses,
508 GAME_PANEL_EMC_LENSES_TIME,
509 &game.panel.emc_lenses_time,
513 GAME_PANEL_EMC_MAGNIFIER,
514 &game.panel.emc_magnifier,
518 GAME_PANEL_EMC_MAGNIFIER_TIME,
519 &game.panel.emc_magnifier_time,
523 GAME_PANEL_BALLOON_SWITCH,
524 &game.panel.balloon_switch,
528 GAME_PANEL_DYNABOMB_NUMBER,
529 &game.panel.dynabomb_number,
533 GAME_PANEL_DYNABOMB_SIZE,
534 &game.panel.dynabomb_size,
538 GAME_PANEL_DYNABOMB_POWER,
539 &game.panel.dynabomb_power,
544 &game.panel.penguins,
548 GAME_PANEL_SOKOBAN_OBJECTS,
549 &game.panel.sokoban_objects,
553 GAME_PANEL_SOKOBAN_FIELDS,
554 &game.panel.sokoban_fields,
558 GAME_PANEL_ROBOT_WHEEL,
559 &game.panel.robot_wheel,
563 GAME_PANEL_CONVEYOR_BELT_1,
564 &game.panel.conveyor_belt[0],
568 GAME_PANEL_CONVEYOR_BELT_2,
569 &game.panel.conveyor_belt[1],
573 GAME_PANEL_CONVEYOR_BELT_3,
574 &game.panel.conveyor_belt[2],
578 GAME_PANEL_CONVEYOR_BELT_4,
579 &game.panel.conveyor_belt[3],
583 GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
584 &game.panel.conveyor_belt_switch[0],
588 GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
589 &game.panel.conveyor_belt_switch[1],
593 GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
594 &game.panel.conveyor_belt_switch[2],
598 GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
599 &game.panel.conveyor_belt_switch[3],
603 GAME_PANEL_MAGIC_WALL,
604 &game.panel.magic_wall,
608 GAME_PANEL_MAGIC_WALL_TIME,
609 &game.panel.magic_wall_time,
613 GAME_PANEL_GRAVITY_STATE,
614 &game.panel.gravity_state,
618 GAME_PANEL_GRAPHIC_1,
619 &game.panel.graphic[0],
623 GAME_PANEL_GRAPHIC_2,
624 &game.panel.graphic[1],
628 GAME_PANEL_GRAPHIC_3,
629 &game.panel.graphic[2],
633 GAME_PANEL_GRAPHIC_4,
634 &game.panel.graphic[3],
638 GAME_PANEL_GRAPHIC_5,
639 &game.panel.graphic[4],
643 GAME_PANEL_GRAPHIC_6,
644 &game.panel.graphic[5],
648 GAME_PANEL_GRAPHIC_7,
649 &game.panel.graphic[6],
653 GAME_PANEL_GRAPHIC_8,
654 &game.panel.graphic[7],
658 GAME_PANEL_ELEMENT_1,
659 &game.panel.element[0],
663 GAME_PANEL_ELEMENT_2,
664 &game.panel.element[1],
668 GAME_PANEL_ELEMENT_3,
669 &game.panel.element[2],
673 GAME_PANEL_ELEMENT_4,
674 &game.panel.element[3],
678 GAME_PANEL_ELEMENT_5,
679 &game.panel.element[4],
683 GAME_PANEL_ELEMENT_6,
684 &game.panel.element[5],
688 GAME_PANEL_ELEMENT_7,
689 &game.panel.element[6],
693 GAME_PANEL_ELEMENT_8,
694 &game.panel.element[7],
698 GAME_PANEL_ELEMENT_COUNT_1,
699 &game.panel.element_count[0],
703 GAME_PANEL_ELEMENT_COUNT_2,
704 &game.panel.element_count[1],
708 GAME_PANEL_ELEMENT_COUNT_3,
709 &game.panel.element_count[2],
713 GAME_PANEL_ELEMENT_COUNT_4,
714 &game.panel.element_count[3],
718 GAME_PANEL_ELEMENT_COUNT_5,
719 &game.panel.element_count[4],
723 GAME_PANEL_ELEMENT_COUNT_6,
724 &game.panel.element_count[5],
728 GAME_PANEL_ELEMENT_COUNT_7,
729 &game.panel.element_count[6],
733 GAME_PANEL_ELEMENT_COUNT_8,
734 &game.panel.element_count[7],
738 GAME_PANEL_CE_SCORE_1,
739 &game.panel.ce_score[0],
743 GAME_PANEL_CE_SCORE_2,
744 &game.panel.ce_score[1],
748 GAME_PANEL_CE_SCORE_3,
749 &game.panel.ce_score[2],
753 GAME_PANEL_CE_SCORE_4,
754 &game.panel.ce_score[3],
758 GAME_PANEL_CE_SCORE_5,
759 &game.panel.ce_score[4],
763 GAME_PANEL_CE_SCORE_6,
764 &game.panel.ce_score[5],
768 GAME_PANEL_CE_SCORE_7,
769 &game.panel.ce_score[6],
773 GAME_PANEL_CE_SCORE_8,
774 &game.panel.ce_score[7],
778 GAME_PANEL_CE_SCORE_1_ELEMENT,
779 &game.panel.ce_score_element[0],
783 GAME_PANEL_CE_SCORE_2_ELEMENT,
784 &game.panel.ce_score_element[1],
788 GAME_PANEL_CE_SCORE_3_ELEMENT,
789 &game.panel.ce_score_element[2],
793 GAME_PANEL_CE_SCORE_4_ELEMENT,
794 &game.panel.ce_score_element[3],
798 GAME_PANEL_CE_SCORE_5_ELEMENT,
799 &game.panel.ce_score_element[4],
803 GAME_PANEL_CE_SCORE_6_ELEMENT,
804 &game.panel.ce_score_element[5],
808 GAME_PANEL_CE_SCORE_7_ELEMENT,
809 &game.panel.ce_score_element[6],
813 GAME_PANEL_CE_SCORE_8_ELEMENT,
814 &game.panel.ce_score_element[7],
818 GAME_PANEL_PLAYER_NAME,
819 &game.panel.player_name,
823 GAME_PANEL_LEVEL_NAME,
824 &game.panel.level_name,
828 GAME_PANEL_LEVEL_AUTHOR,
829 &game.panel.level_author,
842 /* values for delayed check of falling and moving elements and for collision */
843 #define CHECK_DELAY_MOVING 3
844 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
845 #define CHECK_DELAY_COLLISION 2
846 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
848 /* values for initial player move delay (initial delay counter value) */
849 #define INITIAL_MOVE_DELAY_OFF -1
850 #define INITIAL_MOVE_DELAY_ON 0
852 /* values for player movement speed (which is in fact a delay value) */
853 #define MOVE_DELAY_MIN_SPEED 32
854 #define MOVE_DELAY_NORMAL_SPEED 8
855 #define MOVE_DELAY_HIGH_SPEED 4
856 #define MOVE_DELAY_MAX_SPEED 1
858 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
859 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
861 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
862 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
864 /* values for other actions */
865 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN (1)
867 #define MOVE_STEPSIZE_MAX (TILEX)
869 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
872 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
874 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
875 RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
877 RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
879 RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
881 (element_info[e].move_delay_random))
882 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
883 RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
886 RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
888 RND((c)->delay_random))
891 #define GET_VALID_RUNTIME_ELEMENT(e) \
892 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
894 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
895 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
896 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
897 (be) + (e) - EL_SELF)
899 #define GET_PLAYER_FROM_BITS(p) \
900 (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
902 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
903 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
904 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
905 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
906 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
907 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
908 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
909 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
910 RESOLVED_REFERENCE_ELEMENT(be, e) : \
913 #define CAN_GROW_INTO(e) \
914 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
916 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
917 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
921 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
922 (CAN_MOVE_INTO_ACID(e) && \
923 Feld[x][y] == EL_ACID) || \
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
927 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
928 (CAN_MOVE_INTO_ACID(e) && \
929 Feld[x][y] == EL_ACID) || \
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
933 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
935 (CAN_MOVE_INTO_ACID(e) && \
936 Feld[x][y] == EL_ACID) || \
937 (DONT_COLLIDE_WITH(e) && \
939 !PLAYER_ENEMY_PROTECTED(x, y))))
941 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
942 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
944 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
945 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
947 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
948 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
950 #define ANDROID_CAN_CLONE_FIELD(x, y) \
951 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
952 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
954 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
955 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
957 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
958 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
960 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
961 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
964 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
966 #define PIG_CAN_ENTER_FIELD(e, x, y) \
967 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
970 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
971 Feld[x][y] == EL_EM_EXIT_OPEN || \
972 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
973 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974 IS_FOOD_PENGUIN(Feld[x][y])))
975 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
976 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
978 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
979 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
981 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
982 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
984 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
985 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
986 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
988 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
990 #define CE_ENTER_FIELD_COND(e, x, y) \
991 (!IS_PLAYER(x, y) && \
992 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
994 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
995 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
997 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
998 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1000 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
1001 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
1002 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
1003 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1005 /* game button identifiers */
1006 #define GAME_CTRL_ID_STOP 0
1007 #define GAME_CTRL_ID_PAUSE 1
1008 #define GAME_CTRL_ID_PLAY 2
1009 #define SOUND_CTRL_ID_MUSIC 3
1010 #define SOUND_CTRL_ID_LOOPS 4
1011 #define SOUND_CTRL_ID_SIMPLE 5
1013 #define NUM_GAME_BUTTONS 6
1016 /* forward declaration for internal use */
1018 static void CreateField(int, int, int);
1020 static void ResetGfxAnimation(int, int);
1022 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1023 static void AdvanceFrameAndPlayerCounters(int);
1025 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1026 static boolean MovePlayer(struct PlayerInfo *, int, int);
1027 static void ScrollPlayer(struct PlayerInfo *, int);
1028 static void ScrollScreen(struct PlayerInfo *, int);
1030 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1032 static void InitBeltMovement(void);
1033 static void CloseAllOpenTimegates(void);
1034 static void CheckGravityMovement(struct PlayerInfo *);
1035 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1036 static void KillPlayerUnlessEnemyProtected(int, int);
1037 static void KillPlayerUnlessExplosionProtected(int, int);
1039 static void TestIfPlayerTouchesCustomElement(int, int);
1040 static void TestIfElementTouchesCustomElement(int, int);
1041 static void TestIfElementHitsCustomElement(int, int, int);
1043 static void TestIfElementSmashesCustomElement(int, int, int);
1046 static void HandleElementChange(int, int, int);
1047 static void ExecuteCustomElementAction(int, int, int, int);
1048 static boolean ChangeElement(int, int, int, int);
1050 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1051 #define CheckTriggeredElementChange(x, y, e, ev) \
1052 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1053 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
1054 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1055 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
1056 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1057 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
1058 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1060 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1061 #define CheckElementChange(x, y, e, te, ev) \
1062 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1063 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
1064 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1065 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
1066 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1068 static void PlayLevelSound(int, int, int);
1069 static void PlayLevelSoundNearest(int, int, int);
1070 static void PlayLevelSoundAction(int, int, int);
1071 static void PlayLevelSoundElementAction(int, int, int, int);
1072 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1073 static void PlayLevelSoundActionIfLoop(int, int, int);
1074 static void StopLevelSoundActionIfLoop(int, int, int);
1075 static void PlayLevelMusic();
1077 static void MapGameButtons();
1078 static void HandleGameButtons(struct GadgetInfo *);
1080 int AmoebeNachbarNr(int, int);
1081 void AmoebeUmwandeln(int, int);
1082 void ContinueMoving(int, int);
1083 void Bang(int, int);
1084 void InitMovDir(int, int);
1085 void InitAmoebaNr(int, int);
1086 int NewHiScore(void);
1088 void TestIfGoodThingHitsBadThing(int, int, int);
1089 void TestIfBadThingHitsGoodThing(int, int, int);
1090 void TestIfPlayerTouchesBadThing(int, int);
1091 void TestIfPlayerRunsIntoBadThing(int, int, int);
1092 void TestIfBadThingTouchesPlayer(int, int);
1093 void TestIfBadThingRunsIntoPlayer(int, int, int);
1094 void TestIfFriendTouchesBadThing(int, int);
1095 void TestIfBadThingTouchesFriend(int, int);
1096 void TestIfBadThingTouchesOtherBadThing(int, int);
1098 void KillPlayer(struct PlayerInfo *);
1099 void BuryPlayer(struct PlayerInfo *);
1100 void RemovePlayer(struct PlayerInfo *);
1102 boolean SnapField(struct PlayerInfo *, int, int);
1103 boolean DropElement(struct PlayerInfo *);
1105 static int getInvisibleActiveFromInvisibleElement(int);
1106 static int getInvisibleFromInvisibleActiveElement(int);
1108 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1110 /* for detection of endless loops, caused by custom element programming */
1111 /* (using maximal playfield width x 10 is just a rough approximation) */
1112 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
1114 #define RECURSION_LOOP_DETECTION_START(e, rc) \
1116 if (recursion_loop_detected) \
1119 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
1121 recursion_loop_detected = TRUE; \
1122 recursion_loop_element = (e); \
1125 recursion_loop_depth++; \
1128 #define RECURSION_LOOP_DETECTION_END() \
1130 recursion_loop_depth--; \
1133 static int recursion_loop_depth;
1134 static boolean recursion_loop_detected;
1135 static boolean recursion_loop_element;
1138 /* ------------------------------------------------------------------------- */
1139 /* definition of elements that automatically change to other elements after */
1140 /* a specified time, eventually calling a function when changing */
1141 /* ------------------------------------------------------------------------- */
1143 /* forward declaration for changer functions */
1144 static void InitBuggyBase(int, int);
1145 static void WarnBuggyBase(int, int);
1147 static void InitTrap(int, int);
1148 static void ActivateTrap(int, int);
1149 static void ChangeActiveTrap(int, int);
1151 static void InitRobotWheel(int, int);
1152 static void RunRobotWheel(int, int);
1153 static void StopRobotWheel(int, int);
1155 static void InitTimegateWheel(int, int);
1156 static void RunTimegateWheel(int, int);
1158 static void InitMagicBallDelay(int, int);
1159 static void ActivateMagicBall(int, int);
1161 struct ChangingElementInfo
1166 void (*pre_change_function)(int x, int y);
1167 void (*change_function)(int x, int y);
1168 void (*post_change_function)(int x, int y);
1171 static struct ChangingElementInfo change_delay_list[] =
1206 EL_STEEL_EXIT_OPENING,
1214 EL_STEEL_EXIT_CLOSING,
1215 EL_STEEL_EXIT_CLOSED,
1242 EL_EM_STEEL_EXIT_OPENING,
1243 EL_EM_STEEL_EXIT_OPEN,
1250 EL_EM_STEEL_EXIT_CLOSING,
1254 EL_EM_STEEL_EXIT_CLOSED,
1278 EL_SWITCHGATE_OPENING,
1286 EL_SWITCHGATE_CLOSING,
1287 EL_SWITCHGATE_CLOSED,
1294 EL_TIMEGATE_OPENING,
1302 EL_TIMEGATE_CLOSING,
1311 EL_ACID_SPLASH_LEFT,
1319 EL_ACID_SPLASH_RIGHT,
1328 EL_SP_BUGGY_BASE_ACTIVATING,
1335 EL_SP_BUGGY_BASE_ACTIVATING,
1336 EL_SP_BUGGY_BASE_ACTIVE,
1343 EL_SP_BUGGY_BASE_ACTIVE,
1367 EL_ROBOT_WHEEL_ACTIVE,
1375 EL_TIMEGATE_SWITCH_ACTIVE,
1383 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1384 EL_DC_TIMEGATE_SWITCH,
1391 EL_EMC_MAGIC_BALL_ACTIVE,
1392 EL_EMC_MAGIC_BALL_ACTIVE,
1399 EL_EMC_SPRING_BUMPER_ACTIVE,
1400 EL_EMC_SPRING_BUMPER,
1407 EL_DIAGONAL_SHRINKING,
1415 EL_DIAGONAL_GROWING,
1436 int push_delay_fixed, push_delay_random;
1440 { EL_SPRING, 0, 0 },
1441 { EL_BALLOON, 0, 0 },
1443 { EL_SOKOBAN_OBJECT, 2, 0 },
1444 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1445 { EL_SATELLITE, 2, 0 },
1446 { EL_SP_DISK_YELLOW, 2, 0 },
1448 { EL_UNDEFINED, 0, 0 },
1456 move_stepsize_list[] =
1458 { EL_AMOEBA_DROP, 2 },
1459 { EL_AMOEBA_DROPPING, 2 },
1460 { EL_QUICKSAND_FILLING, 1 },
1461 { EL_QUICKSAND_EMPTYING, 1 },
1462 { EL_QUICKSAND_FAST_FILLING, 2 },
1463 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1464 { EL_MAGIC_WALL_FILLING, 2 },
1465 { EL_MAGIC_WALL_EMPTYING, 2 },
1466 { EL_BD_MAGIC_WALL_FILLING, 2 },
1467 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1468 { EL_DC_MAGIC_WALL_FILLING, 2 },
1469 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1471 { EL_UNDEFINED, 0 },
1479 collect_count_list[] =
1482 { EL_BD_DIAMOND, 1 },
1483 { EL_EMERALD_YELLOW, 1 },
1484 { EL_EMERALD_RED, 1 },
1485 { EL_EMERALD_PURPLE, 1 },
1487 { EL_SP_INFOTRON, 1 },
1491 { EL_UNDEFINED, 0 },
1499 access_direction_list[] =
1501 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1502 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1503 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1504 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1505 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1506 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1507 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1508 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1509 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1510 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1511 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1513 { EL_SP_PORT_LEFT, MV_RIGHT },
1514 { EL_SP_PORT_RIGHT, MV_LEFT },
1515 { EL_SP_PORT_UP, MV_DOWN },
1516 { EL_SP_PORT_DOWN, MV_UP },
1517 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1518 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1519 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1520 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1521 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1522 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1523 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1524 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1525 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1526 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1527 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1528 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1529 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1530 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1531 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1533 { EL_UNDEFINED, MV_NONE }
1536 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1538 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1539 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1540 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1541 IS_JUST_CHANGING(x, y))
1543 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1545 /* static variables for playfield scan mode (scanning forward or backward) */
1546 static int playfield_scan_start_x = 0;
1547 static int playfield_scan_start_y = 0;
1548 static int playfield_scan_delta_x = 1;
1549 static int playfield_scan_delta_y = 1;
1551 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1552 (y) >= 0 && (y) <= lev_fieldy - 1; \
1553 (y) += playfield_scan_delta_y) \
1554 for ((x) = playfield_scan_start_x; \
1555 (x) >= 0 && (x) <= lev_fieldx - 1; \
1556 (x) += playfield_scan_delta_x)
1559 void DEBUG_SetMaximumDynamite()
1563 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1564 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1565 local_player->inventory_element[local_player->inventory_size++] =
1570 static void InitPlayfieldScanModeVars()
1572 if (game.use_reverse_scan_direction)
1574 playfield_scan_start_x = lev_fieldx - 1;
1575 playfield_scan_start_y = lev_fieldy - 1;
1577 playfield_scan_delta_x = -1;
1578 playfield_scan_delta_y = -1;
1582 playfield_scan_start_x = 0;
1583 playfield_scan_start_y = 0;
1585 playfield_scan_delta_x = 1;
1586 playfield_scan_delta_y = 1;
1590 static void InitPlayfieldScanMode(int mode)
1592 game.use_reverse_scan_direction =
1593 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1595 InitPlayfieldScanModeVars();
1598 static int get_move_delay_from_stepsize(int move_stepsize)
1601 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1603 /* make sure that stepsize value is always a power of 2 */
1604 move_stepsize = (1 << log_2(move_stepsize));
1606 return TILEX / move_stepsize;
1609 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1612 int player_nr = player->index_nr;
1613 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1614 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1616 /* do no immediately change move delay -- the player might just be moving */
1617 player->move_delay_value_next = move_delay;
1619 /* information if player can move must be set separately */
1620 player->cannot_move = cannot_move;
1624 player->move_delay = game.initial_move_delay[player_nr];
1625 player->move_delay_value = game.initial_move_delay_value[player_nr];
1627 player->move_delay_value_next = -1;
1629 player->move_delay_reset_counter = 0;
1633 void GetPlayerConfig()
1635 GameFrameDelay = setup.game_frame_delay;
1637 if (!audio.sound_available)
1638 setup.sound_simple = FALSE;
1640 if (!audio.loops_available)
1641 setup.sound_loops = FALSE;
1643 if (!audio.music_available)
1644 setup.sound_music = FALSE;
1646 if (!video.fullscreen_available)
1647 setup.fullscreen = FALSE;
1649 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1651 SetAudioMode(setup.sound);
1655 int GetElementFromGroupElement(int element)
1657 if (IS_GROUP_ELEMENT(element))
1659 struct ElementGroupInfo *group = element_info[element].group;
1660 int last_anim_random_frame = gfx.anim_random_frame;
1663 if (group->choice_mode == ANIM_RANDOM)
1664 gfx.anim_random_frame = RND(group->num_elements_resolved);
1666 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1667 group->choice_mode, 0,
1670 if (group->choice_mode == ANIM_RANDOM)
1671 gfx.anim_random_frame = last_anim_random_frame;
1673 group->choice_pos++;
1675 element = group->element_resolved[element_pos];
1681 static void InitPlayerField(int x, int y, int element, boolean init_game)
1683 if (element == EL_SP_MURPHY)
1687 if (stored_player[0].present)
1689 Feld[x][y] = EL_SP_MURPHY_CLONE;
1695 stored_player[0].use_murphy = TRUE;
1697 if (!level.use_artwork_element[0])
1698 stored_player[0].artwork_element = EL_SP_MURPHY;
1701 Feld[x][y] = EL_PLAYER_1;
1707 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1708 int jx = player->jx, jy = player->jy;
1710 player->present = TRUE;
1712 player->block_last_field = (element == EL_SP_MURPHY ?
1713 level.sp_block_last_field :
1714 level.block_last_field);
1716 /* ---------- initialize player's last field block delay --------------- */
1718 /* always start with reliable default value (no adjustment needed) */
1719 player->block_delay_adjustment = 0;
1721 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1722 if (player->block_last_field && element == EL_SP_MURPHY)
1723 player->block_delay_adjustment = 1;
1725 /* special case 2: in game engines before 3.1.1, blocking was different */
1726 if (game.use_block_last_field_bug)
1727 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1729 if (!options.network || player->connected)
1731 player->active = TRUE;
1733 /* remove potentially duplicate players */
1734 if (StorePlayer[jx][jy] == Feld[x][y])
1735 StorePlayer[jx][jy] = 0;
1737 StorePlayer[x][y] = Feld[x][y];
1741 printf("Player %d activated.\n", player->element_nr);
1742 printf("[Local player is %d and currently %s.]\n",
1743 local_player->element_nr,
1744 local_player->active ? "active" : "not active");
1748 Feld[x][y] = EL_EMPTY;
1750 player->jx = player->last_jx = x;
1751 player->jy = player->last_jy = y;
1755 static void InitField(int x, int y, boolean init_game)
1757 int element = Feld[x][y];
1766 InitPlayerField(x, y, element, init_game);
1769 case EL_SOKOBAN_FIELD_PLAYER:
1770 element = Feld[x][y] = EL_PLAYER_1;
1771 InitField(x, y, init_game);
1773 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1774 InitField(x, y, init_game);
1777 case EL_SOKOBAN_FIELD_EMPTY:
1778 local_player->sokobanfields_still_needed++;
1782 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1783 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1784 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1785 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1786 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1787 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1788 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1789 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1790 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1791 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1800 case EL_SPACESHIP_RIGHT:
1801 case EL_SPACESHIP_UP:
1802 case EL_SPACESHIP_LEFT:
1803 case EL_SPACESHIP_DOWN:
1804 case EL_BD_BUTTERFLY:
1805 case EL_BD_BUTTERFLY_RIGHT:
1806 case EL_BD_BUTTERFLY_UP:
1807 case EL_BD_BUTTERFLY_LEFT:
1808 case EL_BD_BUTTERFLY_DOWN:
1810 case EL_BD_FIREFLY_RIGHT:
1811 case EL_BD_FIREFLY_UP:
1812 case EL_BD_FIREFLY_LEFT:
1813 case EL_BD_FIREFLY_DOWN:
1814 case EL_PACMAN_RIGHT:
1816 case EL_PACMAN_LEFT:
1817 case EL_PACMAN_DOWN:
1819 case EL_YAMYAM_LEFT:
1820 case EL_YAMYAM_RIGHT:
1822 case EL_YAMYAM_DOWN:
1823 case EL_DARK_YAMYAM:
1826 case EL_SP_SNIKSNAK:
1827 case EL_SP_ELECTRON:
1836 case EL_AMOEBA_FULL:
1841 case EL_AMOEBA_DROP:
1842 if (y == lev_fieldy - 1)
1844 Feld[x][y] = EL_AMOEBA_GROWING;
1845 Store[x][y] = EL_AMOEBA_WET;
1849 case EL_DYNAMITE_ACTIVE:
1850 case EL_SP_DISK_RED_ACTIVE:
1851 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1852 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1853 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1854 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1855 MovDelay[x][y] = 96;
1858 case EL_EM_DYNAMITE_ACTIVE:
1859 MovDelay[x][y] = 32;
1863 local_player->lights_still_needed++;
1867 local_player->friends_still_needed++;
1872 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1875 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1876 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1877 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1878 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1879 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1880 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1881 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1882 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1883 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1884 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1885 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1886 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1889 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1890 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1891 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1893 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1895 game.belt_dir[belt_nr] = belt_dir;
1896 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1898 else /* more than one switch -- set it like the first switch */
1900 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1905 #if !USE_BOTH_SWITCHGATE_SWITCHES
1906 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1908 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1911 case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1913 Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1917 case EL_LIGHT_SWITCH_ACTIVE:
1919 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1922 case EL_INVISIBLE_STEELWALL:
1923 case EL_INVISIBLE_WALL:
1924 case EL_INVISIBLE_SAND:
1925 if (game.light_time_left > 0 ||
1926 game.lenses_time_left > 0)
1927 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1930 case EL_EMC_MAGIC_BALL:
1931 if (game.ball_state)
1932 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1935 case EL_EMC_MAGIC_BALL_SWITCH:
1936 if (game.ball_state)
1937 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1941 if (IS_CUSTOM_ELEMENT(element))
1943 if (CAN_MOVE(element))
1946 #if USE_NEW_CUSTOM_VALUE
1947 if (!element_info[element].use_last_ce_value || init_game)
1948 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1951 else if (IS_GROUP_ELEMENT(element))
1953 Feld[x][y] = GetElementFromGroupElement(element);
1955 InitField(x, y, init_game);
1962 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1965 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1967 InitField(x, y, init_game);
1969 /* not needed to call InitMovDir() -- already done by InitField()! */
1970 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1971 CAN_MOVE(Feld[x][y]))
1975 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1977 int old_element = Feld[x][y];
1979 InitField(x, y, init_game);
1981 /* not needed to call InitMovDir() -- already done by InitField()! */
1982 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1983 CAN_MOVE(old_element) &&
1984 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1987 /* this case is in fact a combination of not less than three bugs:
1988 first, it calls InitMovDir() for elements that can move, although this is
1989 already done by InitField(); then, it checks the element that was at this
1990 field _before_ the call to InitField() (which can change it); lastly, it
1991 was not called for "mole with direction" elements, which were treated as
1992 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1998 static int get_key_element_from_nr(int key_nr)
2000 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2001 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2002 EL_EM_KEY_1 : EL_KEY_1);
2004 return key_base_element + key_nr;
2007 static int get_next_dropped_element(struct PlayerInfo *player)
2009 return (player->inventory_size > 0 ?
2010 player->inventory_element[player->inventory_size - 1] :
2011 player->inventory_infinite_element != EL_UNDEFINED ?
2012 player->inventory_infinite_element :
2013 player->dynabombs_left > 0 ?
2014 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2018 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2020 /* pos >= 0: get element from bottom of the stack;
2021 pos < 0: get element from top of the stack */
2025 int min_inventory_size = -pos;
2026 int inventory_pos = player->inventory_size - min_inventory_size;
2027 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2029 return (player->inventory_size >= min_inventory_size ?
2030 player->inventory_element[inventory_pos] :
2031 player->inventory_infinite_element != EL_UNDEFINED ?
2032 player->inventory_infinite_element :
2033 player->dynabombs_left >= min_dynabombs_left ?
2034 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2039 int min_dynabombs_left = pos + 1;
2040 int min_inventory_size = pos + 1 - player->dynabombs_left;
2041 int inventory_pos = pos - player->dynabombs_left;
2043 return (player->inventory_infinite_element != EL_UNDEFINED ?
2044 player->inventory_infinite_element :
2045 player->dynabombs_left >= min_dynabombs_left ?
2046 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2047 player->inventory_size >= min_inventory_size ?
2048 player->inventory_element[inventory_pos] :
2053 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2055 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2056 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2059 if (gpo1->sort_priority != gpo2->sort_priority)
2060 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2062 compare_result = gpo1->nr - gpo2->nr;
2064 return compare_result;
2067 void InitGameControlValues()
2071 for (i = 0; game_panel_controls[i].nr != -1; i++)
2073 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2074 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2075 struct TextPosInfo *pos = gpc->pos;
2077 int type = gpc->type;
2081 Error(ERR_INFO, "'game_panel_controls' structure corrupted");
2082 Error(ERR_EXIT, "this should not happen -- please debug");
2085 /* force update of game controls after initialization */
2086 gpc->value = gpc->last_value = -1;
2087 gpc->frame = gpc->last_frame = -1;
2088 gpc->gfx_frame = -1;
2090 /* determine panel value width for later calculation of alignment */
2091 if (type == TYPE_INTEGER || type == TYPE_STRING)
2093 pos->width = pos->size * getFontWidth(pos->font);
2094 pos->height = getFontHeight(pos->font);
2096 else if (type == TYPE_ELEMENT)
2098 pos->width = pos->size;
2099 pos->height = pos->size;
2102 /* fill structure for game panel draw order */
2104 gpo->sort_priority = pos->sort_priority;
2107 /* sort game panel controls according to sort_priority and control number */
2108 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2109 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2112 void UpdatePlayfieldElementCount()
2114 boolean use_element_count = FALSE;
2117 /* first check if it is needed at all to calculate playfield element count */
2118 for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2119 if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2120 use_element_count = TRUE;
2122 if (!use_element_count)
2125 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2126 element_info[i].element_count = 0;
2128 SCAN_PLAYFIELD(x, y)
2130 element_info[Feld[x][y]].element_count++;
2133 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2134 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2135 if (IS_IN_GROUP(j, i))
2136 element_info[EL_GROUP_START + i].element_count +=
2137 element_info[j].element_count;
2140 void UpdateGameControlValues()
2143 int time = (local_player->LevelSolved ?
2144 local_player->LevelSolved_CountingTime :
2145 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2146 level.native_em_level->lev->time :
2147 level.time == 0 ? TimePlayed : TimeLeft);
2148 int score = (local_player->LevelSolved ?
2149 local_player->LevelSolved_CountingScore :
2150 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2151 level.native_em_level->lev->score :
2152 local_player->score);
2153 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2154 level.native_em_level->lev->required :
2155 local_player->gems_still_needed);
2156 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2157 level.native_em_level->lev->required > 0 :
2158 local_player->gems_still_needed > 0 ||
2159 local_player->sokobanfields_still_needed > 0 ||
2160 local_player->lights_still_needed > 0);
2162 UpdatePlayfieldElementCount();
2164 /* update game panel control values */
2166 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2167 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2169 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2170 for (i = 0; i < MAX_NUM_KEYS; i++)
2171 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2172 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2173 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2175 if (game.centered_player_nr == -1)
2177 for (i = 0; i < MAX_PLAYERS; i++)
2179 for (k = 0; k < MAX_NUM_KEYS; k++)
2181 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2183 if (level.native_em_level->ply[i]->keys & (1 << k))
2184 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2185 get_key_element_from_nr(k);
2187 else if (stored_player[i].key[k])
2188 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2189 get_key_element_from_nr(k);
2192 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2193 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2194 level.native_em_level->ply[i]->dynamite;
2196 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2197 stored_player[i].inventory_size;
2199 if (stored_player[i].num_white_keys > 0)
2200 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2203 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2204 stored_player[i].num_white_keys;
2209 int player_nr = game.centered_player_nr;
2211 for (k = 0; k < MAX_NUM_KEYS; k++)
2213 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2215 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2216 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2217 get_key_element_from_nr(k);
2219 else if (stored_player[player_nr].key[k])
2220 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2221 get_key_element_from_nr(k);
2224 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2225 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2226 level.native_em_level->ply[player_nr]->dynamite;
2228 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2229 stored_player[player_nr].inventory_size;
2231 if (stored_player[player_nr].num_white_keys > 0)
2232 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2234 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2235 stored_player[player_nr].num_white_keys;
2238 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2240 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2241 get_inventory_element_from_pos(local_player, i);
2242 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2243 get_inventory_element_from_pos(local_player, -i - 1);
2246 game_panel_controls[GAME_PANEL_SCORE].value = score;
2248 game_panel_controls[GAME_PANEL_TIME].value = time;
2250 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2251 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2252 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2254 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2255 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2257 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2258 local_player->shield_normal_time_left;
2259 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2260 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2262 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2263 local_player->shield_deadly_time_left;
2265 game_panel_controls[GAME_PANEL_EXIT].value =
2266 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2268 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2269 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2270 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2271 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2272 EL_EMC_MAGIC_BALL_SWITCH);
2274 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2275 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2276 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2277 game.light_time_left;
2279 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2280 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2281 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2282 game.timegate_time_left;
2284 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2285 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2287 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2288 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2289 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2290 game.lenses_time_left;
2292 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2293 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2294 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2295 game.magnify_time_left;
2297 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2298 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2299 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2300 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2301 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2302 EL_BALLOON_SWITCH_NONE);
2304 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2305 local_player->dynabomb_count;
2306 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2307 local_player->dynabomb_size;
2308 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2309 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2311 game_panel_controls[GAME_PANEL_PENGUINS].value =
2312 local_player->friends_still_needed;
2314 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2315 local_player->sokobanfields_still_needed;
2316 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2317 local_player->sokobanfields_still_needed;
2319 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2320 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2322 for (i = 0; i < NUM_BELTS; i++)
2324 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2325 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2326 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2327 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2328 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2331 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2332 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2333 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2334 game.magic_wall_time_left;
2336 #if USE_PLAYER_GRAVITY
2337 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2338 local_player->gravity;
2340 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2343 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2344 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2346 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2347 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2348 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2349 game.panel.element[i].id : EL_UNDEFINED);
2351 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2352 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2353 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2354 element_info[game.panel.element_count[i].id].element_count : 0);
2356 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2357 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2358 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2359 element_info[game.panel.ce_score[i].id].collect_score : 0);
2361 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2362 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2363 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2364 element_info[game.panel.ce_score_element[i].id].collect_score :
2367 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2368 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2369 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2371 /* update game panel control frames */
2373 for (i = 0; game_panel_controls[i].nr != -1; i++)
2375 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2377 if (gpc->type == TYPE_ELEMENT)
2379 int last_anim_random_frame = gfx.anim_random_frame;
2380 int element = gpc->value;
2381 int graphic = el2panelimg(element);
2383 if (gpc->value != gpc->last_value)
2386 gpc->gfx_random = INIT_GFX_RANDOM();
2392 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2393 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2394 gpc->gfx_random = INIT_GFX_RANDOM();
2397 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2398 gfx.anim_random_frame = gpc->gfx_random;
2400 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2401 gpc->gfx_frame = element_info[element].collect_score;
2403 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2406 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2407 gfx.anim_random_frame = last_anim_random_frame;
2412 void DisplayGameControlValues()
2414 boolean redraw_panel = FALSE;
2417 for (i = 0; game_panel_controls[i].nr != -1; i++)
2419 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2421 if (PANEL_DEACTIVATED(gpc->pos))
2424 if (gpc->value == gpc->last_value &&
2425 gpc->frame == gpc->last_frame)
2428 redraw_panel = TRUE;
2434 /* copy default game door content to main double buffer */
2435 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2436 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2438 /* redraw game control buttons */
2440 RedrawGameButtons();
2446 game_status = GAME_MODE_PSEUDO_PANEL;
2449 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2451 for (i = 0; game_panel_controls[i].nr != -1; i++)
2455 int nr = game_panel_order[i].nr;
2456 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2458 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2461 struct TextPosInfo *pos = gpc->pos;
2462 int type = gpc->type;
2463 int value = gpc->value;
2464 int frame = gpc->frame;
2466 int last_value = gpc->last_value;
2467 int last_frame = gpc->last_frame;
2469 int size = pos->size;
2470 int font = pos->font;
2471 boolean draw_masked = pos->draw_masked;
2472 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2474 if (PANEL_DEACTIVATED(pos))
2478 if (value == last_value && frame == last_frame)
2482 gpc->last_value = value;
2483 gpc->last_frame = frame;
2486 printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2489 if (type == TYPE_INTEGER)
2491 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2492 nr == GAME_PANEL_TIME)
2494 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2496 if (use_dynamic_size) /* use dynamic number of digits */
2498 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2499 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2500 int size2 = size1 + 1;
2501 int font1 = pos->font;
2502 int font2 = pos->font_alt;
2504 size = (value < value_change ? size1 : size2);
2505 font = (value < value_change ? font1 : font2);
2508 /* clear background if value just changed its size (dynamic digits) */
2509 if ((last_value < value_change) != (value < value_change))
2511 int width1 = size1 * getFontWidth(font1);
2512 int width2 = size2 * getFontWidth(font2);
2513 int max_width = MAX(width1, width2);
2514 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2516 pos->width = max_width;
2518 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2519 max_width, max_height);
2526 /* correct text size if "digits" is zero or less */
2528 size = strlen(int2str(value, size));
2530 /* dynamically correct text alignment */
2531 pos->width = size * getFontWidth(font);
2534 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2535 int2str(value, size), font, mask_mode);
2537 else if (type == TYPE_ELEMENT)
2539 int element, graphic;
2543 int dst_x = PANEL_XPOS(pos);
2544 int dst_y = PANEL_YPOS(pos);
2547 if (value != EL_UNDEFINED && value != EL_EMPTY)
2550 graphic = el2panelimg(value);
2552 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2555 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2559 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2562 width = graphic_info[graphic].width * size / TILESIZE;
2563 height = graphic_info[graphic].height * size / TILESIZE;
2567 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2568 dst_x - src_x, dst_y - src_y);
2569 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2574 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2579 if (value == EL_UNDEFINED || value == EL_EMPTY)
2581 element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2582 graphic = el2panelimg(element);
2584 src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2585 src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2586 src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2591 graphic = el2panelimg(value);
2593 getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2596 width = graphic_info[graphic].width * size / TILESIZE;
2597 height = graphic_info[graphic].height * size / TILESIZE;
2599 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2602 else if (type == TYPE_STRING)
2604 boolean active = (value != 0);
2605 char *state_normal = "off";
2606 char *state_active = "on";
2607 char *state = (active ? state_active : state_normal);
2608 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2609 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2610 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2611 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2613 if (nr == GAME_PANEL_GRAVITY_STATE)
2615 int font1 = pos->font; /* (used for normal state) */
2616 int font2 = pos->font_alt; /* (used for active state) */
2618 int size1 = strlen(state_normal);
2619 int size2 = strlen(state_active);
2620 int width1 = size1 * getFontWidth(font1);
2621 int width2 = size2 * getFontWidth(font2);
2622 int max_width = MAX(width1, width2);
2623 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2625 pos->width = max_width;
2627 /* clear background for values that may have changed its size */
2628 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2629 max_width, max_height);
2632 font = (active ? font2 : font1);
2642 /* don't truncate output if "chars" is zero or less */
2645 /* dynamically correct text alignment */
2646 pos->width = size * getFontWidth(font);
2650 s_cut = getStringCopyN(s, size);
2652 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2653 s_cut, font, mask_mode);
2659 redraw_mask |= REDRAW_DOOR_1;
2662 game_status = GAME_MODE_PLAYING;
2665 void DrawGameValue_Emeralds(int value)
2667 struct TextPosInfo *pos = &game.panel.gems;
2669 int font_nr = pos->font;
2671 int font_nr = FONT_TEXT_2;
2673 int font_width = getFontWidth(font_nr);
2674 int chars = pos->size;
2677 return; /* !!! USE NEW STUFF !!! */
2680 if (PANEL_DEACTIVATED(pos))
2683 pos->width = chars * font_width;
2685 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2688 void DrawGameValue_Dynamite(int value)
2690 struct TextPosInfo *pos = &game.panel.inventory_count;
2692 int font_nr = pos->font;
2694 int font_nr = FONT_TEXT_2;
2696 int font_width = getFontWidth(font_nr);
2697 int chars = pos->size;
2700 return; /* !!! USE NEW STUFF !!! */
2703 if (PANEL_DEACTIVATED(pos))
2706 pos->width = chars * font_width;
2708 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2711 void DrawGameValue_Score(int value)
2713 struct TextPosInfo *pos = &game.panel.score;
2715 int font_nr = pos->font;
2717 int font_nr = FONT_TEXT_2;
2719 int font_width = getFontWidth(font_nr);
2720 int chars = pos->size;
2723 return; /* !!! USE NEW STUFF !!! */
2726 if (PANEL_DEACTIVATED(pos))
2729 pos->width = chars * font_width;
2731 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2734 void DrawGameValue_Time(int value)
2736 struct TextPosInfo *pos = &game.panel.time;
2737 static int last_value = -1;
2740 int chars = pos->size;
2742 int font1_nr = pos->font;
2743 int font2_nr = pos->font_alt;
2745 int font1_nr = FONT_TEXT_2;
2746 int font2_nr = FONT_TEXT_1;
2748 int font_nr = font1_nr;
2749 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2752 return; /* !!! USE NEW STUFF !!! */
2755 if (PANEL_DEACTIVATED(pos))
2758 if (use_dynamic_chars) /* use dynamic number of chars */
2760 chars = (value < 1000 ? chars1 : chars2);
2761 font_nr = (value < 1000 ? font1_nr : font2_nr);
2764 /* clear background if value just changed its size (dynamic chars only) */
2765 if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2767 int width1 = chars1 * getFontWidth(font1_nr);
2768 int width2 = chars2 * getFontWidth(font2_nr);
2769 int max_width = MAX(width1, width2);
2770 int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2772 pos->width = max_width;
2774 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2775 max_width, max_height);
2778 pos->width = chars * getFontWidth(font_nr);
2780 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2785 void DrawGameValue_Level(int value)
2787 struct TextPosInfo *pos = &game.panel.level_number;
2790 int chars = pos->size;
2792 int font1_nr = pos->font;
2793 int font2_nr = pos->font_alt;
2795 int font1_nr = FONT_TEXT_2;
2796 int font2_nr = FONT_TEXT_1;
2798 int font_nr = font1_nr;
2799 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2802 return; /* !!! USE NEW STUFF !!! */
2805 if (PANEL_DEACTIVATED(pos))
2808 if (use_dynamic_chars) /* use dynamic number of chars */
2810 chars = (level_nr < 100 ? chars1 : chars2);
2811 font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2814 pos->width = chars * getFontWidth(font_nr);
2816 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2819 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2822 struct TextPosInfo *pos = &game.panel.keys;
2825 int base_key_graphic = EL_KEY_1;
2830 return; /* !!! USE NEW STUFF !!! */
2834 if (PANEL_DEACTIVATED(pos))
2839 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2840 base_key_graphic = EL_EM_KEY_1;
2844 pos->width = 4 * MINI_TILEX;
2848 for (i = 0; i < MAX_NUM_KEYS; i++)
2850 /* currently only 4 of 8 possible keys are displayed */
2851 for (i = 0; i < STD_NUM_KEYS; i++)
2855 struct TextPosInfo *pos = &game.panel.key[i];
2857 int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2858 int src_y = DOOR_GFX_PAGEY1 + 123;
2860 int dst_x = PANEL_XPOS(pos);
2861 int dst_y = PANEL_YPOS(pos);
2863 int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2864 int dst_y = PANEL_YPOS(pos);
2868 int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2869 level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2871 int graphic = el2edimg(element);
2875 if (PANEL_DEACTIVATED(pos))
2880 /* masked blit with tiles from half-size scaled bitmap does not work yet
2881 (no mask bitmap created for these sizes after loading and scaling) --
2882 solution: load without creating mask, scale, then create final mask */
2884 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2885 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2890 int graphic = el2edimg(base_key_graphic + i);
2895 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2897 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2898 dst_x - src_x, dst_y - src_y);
2899 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2905 DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2907 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2908 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2911 DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
2913 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2914 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2922 void DrawGameValue_Emeralds(int value)
2924 int font_nr = FONT_TEXT_2;
2925 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2927 if (PANEL_DEACTIVATED(game.panel.gems))
2930 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
2933 void DrawGameValue_Dynamite(int value)
2935 int font_nr = FONT_TEXT_2;
2936 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2938 if (PANEL_DEACTIVATED(game.panel.inventory_count))
2941 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
2944 void DrawGameValue_Score(int value)
2946 int font_nr = FONT_TEXT_2;
2947 int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
2949 if (PANEL_DEACTIVATED(game.panel.score))
2952 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
2955 void DrawGameValue_Time(int value)
2957 int font1_nr = FONT_TEXT_2;
2959 int font2_nr = FONT_TEXT_1;
2961 int font2_nr = FONT_LEVEL_NUMBER;
2963 int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
2964 int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
2966 if (PANEL_DEACTIVATED(game.panel.time))
2969 /* clear background if value just changed its size */
2970 if (value == 999 || value == 1000)
2971 ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
2974 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
2976 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
2979 void DrawGameValue_Level(int value)
2981 int font1_nr = FONT_TEXT_2;
2983 int font2_nr = FONT_TEXT_1;
2985 int font2_nr = FONT_LEVEL_NUMBER;
2988 if (PANEL_DEACTIVATED(game.panel.level))
2992 DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
2994 DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
2997 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2999 int base_key_graphic = EL_KEY_1;
3002 if (PANEL_DEACTIVATED(game.panel.keys))
3005 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3006 base_key_graphic = EL_EM_KEY_1;
3008 /* currently only 4 of 8 possible keys are displayed */
3009 for (i = 0; i < STD_NUM_KEYS; i++)
3011 int x = XX_KEYS + i * MINI_TILEX;
3015 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3017 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3018 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3024 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3027 int key[MAX_NUM_KEYS];
3030 /* prevent EM engine from updating time/score values parallel to GameWon() */
3031 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3032 local_player->LevelSolved)
3035 for (i = 0; i < MAX_NUM_KEYS; i++)
3036 key[i] = key_bits & (1 << i);
3038 DrawGameValue_Level(level_nr);
3040 DrawGameValue_Emeralds(emeralds);
3041 DrawGameValue_Dynamite(dynamite);
3042 DrawGameValue_Score(score);
3043 DrawGameValue_Time(time);
3045 DrawGameValue_Keys(key);
3048 void UpdateGameDoorValues()
3050 UpdateGameControlValues();
3053 void DrawGameDoorValues()
3055 DisplayGameControlValues();
3058 void DrawGameDoorValues_OLD()
3060 int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
3061 int dynamite_value = 0;
3062 int score_value = (local_player->LevelSolved ? local_player->score_final :
3063 local_player->score);
3064 int gems_value = local_player->gems_still_needed;
3068 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3070 DrawGameDoorValues_EM();
3075 if (game.centered_player_nr == -1)
3077 for (i = 0; i < MAX_PLAYERS; i++)
3079 for (j = 0; j < MAX_NUM_KEYS; j++)
3080 if (stored_player[i].key[j])
3081 key_bits |= (1 << j);
3083 dynamite_value += stored_player[i].inventory_size;
3088 int player_nr = game.centered_player_nr;
3090 for (i = 0; i < MAX_NUM_KEYS; i++)
3091 if (stored_player[player_nr].key[i])
3092 key_bits |= (1 << i);
3094 dynamite_value = stored_player[player_nr].inventory_size;
3097 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3103 =============================================================================
3105 -----------------------------------------------------------------------------
3106 initialize game engine due to level / tape version number
3107 =============================================================================
3110 static void InitGameEngine()
3112 int i, j, k, l, x, y;
3114 /* set game engine from tape file when re-playing, else from level file */
3115 game.engine_version = (tape.playing ? tape.engine_version :
3116 level.game_version);
3118 /* ---------------------------------------------------------------------- */
3119 /* set flags for bugs and changes according to active game engine version */
3120 /* ---------------------------------------------------------------------- */
3123 Summary of bugfix/change:
3124 Fixed handling for custom elements that change when pushed by the player.
3126 Fixed/changed in version:
3130 Before 3.1.0, custom elements that "change when pushing" changed directly
3131 after the player started pushing them (until then handled in "DigField()").
3132 Since 3.1.0, these custom elements are not changed until the "pushing"
3133 move of the element is finished (now handled in "ContinueMoving()").
3135 Affected levels/tapes:
3136 The first condition is generally needed for all levels/tapes before version
3137 3.1.0, which might use the old behaviour before it was changed; known tapes
3138 that are affected are some tapes from the level set "Walpurgis Gardens" by
3140 The second condition is an exception from the above case and is needed for
3141 the special case of tapes recorded with game (not engine!) version 3.1.0 or
3142 above (including some development versions of 3.1.0), but before it was
3143 known that this change would break tapes like the above and was fixed in
3144 3.1.1, so that the changed behaviour was active although the engine version
3145 while recording maybe was before 3.1.0. There is at least one tape that is
3146 affected by this exception, which is the tape for the one-level set "Bug
3147 Machine" by Juergen Bonhagen.
3150 game.use_change_when_pushing_bug =
3151 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3153 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3154 tape.game_version < VERSION_IDENT(3,1,1,0)));
3157 Summary of bugfix/change:
3158 Fixed handling for blocking the field the player leaves when moving.
3160 Fixed/changed in version:
3164 Before 3.1.1, when "block last field when moving" was enabled, the field
3165 the player is leaving when moving was blocked for the time of the move,
3166 and was directly unblocked afterwards. This resulted in the last field
3167 being blocked for exactly one less than the number of frames of one player
3168 move. Additionally, even when blocking was disabled, the last field was
3169 blocked for exactly one frame.
3170 Since 3.1.1, due to changes in player movement handling, the last field
3171 is not blocked at all when blocking is disabled. When blocking is enabled,
3172 the last field is blocked for exactly the number of frames of one player
3173 move. Additionally, if the player is Murphy, the hero of Supaplex, the
3174 last field is blocked for exactly one more than the number of frames of
3177 Affected levels/tapes:
3178 (!!! yet to be determined -- probably many !!!)
3181 game.use_block_last_field_bug =
3182 (game.engine_version < VERSION_IDENT(3,1,1,0));
3185 Summary of bugfix/change:
3186 Changed behaviour of CE changes with multiple changes per single frame.
3188 Fixed/changed in version:
3192 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3193 This resulted in race conditions where CEs seem to behave strange in some
3194 situations (where triggered CE changes were just skipped because there was
3195 already a CE change on that tile in the playfield in that engine frame).
3196 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3197 (The number of changes per frame must be limited in any case, because else
3198 it is easily possible to define CE changes that would result in an infinite
3199 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3200 should be set large enough so that it would only be reached in cases where
3201 the corresponding CE change conditions run into a loop. Therefore, it seems
3202 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3203 maximal number of change pages for custom elements.)
3205 Affected levels/tapes:
3209 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3210 game.max_num_changes_per_frame = 1;
3212 game.max_num_changes_per_frame =
3213 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3216 /* ---------------------------------------------------------------------- */
3218 /* default scan direction: scan playfield from top/left to bottom/right */
3219 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3221 /* dynamically adjust element properties according to game engine version */
3222 InitElementPropertiesEngine(game.engine_version);
3225 printf("level %d: level version == %06d\n", level_nr, level.game_version);
3226 printf(" tape version == %06d [%s] [file: %06d]\n",
3227 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3229 printf(" => game.engine_version == %06d\n", game.engine_version);
3232 /* ---------- initialize player's initial move delay --------------------- */
3234 /* dynamically adjust player properties according to level information */
3235 for (i = 0; i < MAX_PLAYERS; i++)
3236 game.initial_move_delay_value[i] =
3237 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3239 /* dynamically adjust player properties according to game engine version */
3240 for (i = 0; i < MAX_PLAYERS; i++)
3241 game.initial_move_delay[i] =
3242 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3243 game.initial_move_delay_value[i] : 0);
3245 /* ---------- initialize player's initial push delay --------------------- */
3247 /* dynamically adjust player properties according to game engine version */
3248 game.initial_push_delay_value =
3249 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3251 /* ---------- initialize changing elements ------------------------------- */
3253 /* initialize changing elements information */
3254 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3256 struct ElementInfo *ei = &element_info[i];
3258 /* this pointer might have been changed in the level editor */
3259 ei->change = &ei->change_page[0];
3261 if (!IS_CUSTOM_ELEMENT(i))
3263 ei->change->target_element = EL_EMPTY_SPACE;
3264 ei->change->delay_fixed = 0;
3265 ei->change->delay_random = 0;
3266 ei->change->delay_frames = 1;
3269 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3271 ei->has_change_event[j] = FALSE;
3273 ei->event_page_nr[j] = 0;
3274 ei->event_page[j] = &ei->change_page[0];
3278 /* add changing elements from pre-defined list */
3279 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3281 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3282 struct ElementInfo *ei = &element_info[ch_delay->element];
3284 ei->change->target_element = ch_delay->target_element;
3285 ei->change->delay_fixed = ch_delay->change_delay;
3287 ei->change->pre_change_function = ch_delay->pre_change_function;
3288 ei->change->change_function = ch_delay->change_function;
3289 ei->change->post_change_function = ch_delay->post_change_function;
3291 ei->change->can_change = TRUE;
3292 ei->change->can_change_or_has_action = TRUE;
3294 ei->has_change_event[CE_DELAY] = TRUE;
3296 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3297 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3300 /* ---------- initialize internal run-time variables ------------- */
3302 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3304 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3306 for (j = 0; j < ei->num_change_pages; j++)
3308 ei->change_page[j].can_change_or_has_action =
3309 (ei->change_page[j].can_change |
3310 ei->change_page[j].has_action);
3314 /* add change events from custom element configuration */
3315 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3317 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3319 for (j = 0; j < ei->num_change_pages; j++)
3321 if (!ei->change_page[j].can_change_or_has_action)
3324 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3326 /* only add event page for the first page found with this event */
3327 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3329 ei->has_change_event[k] = TRUE;
3331 ei->event_page_nr[k] = j;
3332 ei->event_page[k] = &ei->change_page[j];
3338 /* ---------- initialize run-time trigger player and element ------------- */
3340 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3342 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3344 for (j = 0; j < ei->num_change_pages; j++)
3346 ei->change_page[j].actual_trigger_element = EL_EMPTY;
3347 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
3348 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_1;
3349 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3350 ei->change_page[j].actual_trigger_ce_value = 0;
3351 ei->change_page[j].actual_trigger_ce_score = 0;
3355 /* ---------- initialize trigger events ---------------------------------- */
3357 /* initialize trigger events information */
3358 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3359 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3360 trigger_events[i][j] = FALSE;
3362 /* add trigger events from element change event properties */
3363 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3365 struct ElementInfo *ei = &element_info[i];
3367 for (j = 0; j < ei->num_change_pages; j++)
3369 if (!ei->change_page[j].can_change_or_has_action)
3372 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3374 int trigger_element = ei->change_page[j].trigger_element;
3376 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3378 if (ei->change_page[j].has_event[k])
3380 if (IS_GROUP_ELEMENT(trigger_element))
3382 struct ElementGroupInfo *group =
3383 element_info[trigger_element].group;
3385 for (l = 0; l < group->num_elements_resolved; l++)
3386 trigger_events[group->element_resolved[l]][k] = TRUE;
3388 else if (trigger_element == EL_ANY_ELEMENT)
3389 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3390 trigger_events[l][k] = TRUE;
3392 trigger_events[trigger_element][k] = TRUE;
3399 /* ---------- initialize push delay -------------------------------------- */
3401 /* initialize push delay values to default */
3402 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3404 if (!IS_CUSTOM_ELEMENT(i))
3406 /* set default push delay values (corrected since version 3.0.7-1) */
3407 if (game.engine_version < VERSION_IDENT(3,0,7,1))
3409 element_info[i].push_delay_fixed = 2;
3410 element_info[i].push_delay_random = 8;
3414 element_info[i].push_delay_fixed = 8;
3415 element_info[i].push_delay_random = 8;
3420 /* set push delay value for certain elements from pre-defined list */
3421 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3423 int e = push_delay_list[i].element;
3425 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
3426 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3429 /* set push delay value for Supaplex elements for newer engine versions */
3430 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3432 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3434 if (IS_SP_ELEMENT(i))
3436 /* set SP push delay to just enough to push under a falling zonk */
3437 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3439 element_info[i].push_delay_fixed = delay;
3440 element_info[i].push_delay_random = 0;
3445 /* ---------- initialize move stepsize ----------------------------------- */
3447 /* initialize move stepsize values to default */
3448 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3449 if (!IS_CUSTOM_ELEMENT(i))
3450 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3452 /* set move stepsize value for certain elements from pre-defined list */
3453 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3455 int e = move_stepsize_list[i].element;
3457 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3460 /* ---------- initialize collect score ----------------------------------- */
3462 /* initialize collect score values for custom elements from initial value */
3463 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3464 if (IS_CUSTOM_ELEMENT(i))
3465 element_info[i].collect_score = element_info[i].collect_score_initial;
3467 /* ---------- initialize collect count ----------------------------------- */
3469 /* initialize collect count values for non-custom elements */
3470 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3471 if (!IS_CUSTOM_ELEMENT(i))
3472 element_info[i].collect_count_initial = 0;
3474 /* add collect count values for all elements from pre-defined list */
3475 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3476 element_info[collect_count_list[i].element].collect_count_initial =
3477 collect_count_list[i].count;
3479 /* ---------- initialize access direction -------------------------------- */
3481 /* initialize access direction values to default (access from every side) */
3482 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3483 if (!IS_CUSTOM_ELEMENT(i))
3484 element_info[i].access_direction = MV_ALL_DIRECTIONS;
3486 /* set access direction value for certain elements from pre-defined list */
3487 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3488 element_info[access_direction_list[i].element].access_direction =
3489 access_direction_list[i].direction;
3491 /* ---------- initialize explosion content ------------------------------- */
3492 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3494 if (IS_CUSTOM_ELEMENT(i))
3497 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3499 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3501 element_info[i].content.e[x][y] =
3502 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3503 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3504 i == EL_PLAYER_3 ? EL_EMERALD :
3505 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3506 i == EL_MOLE ? EL_EMERALD_RED :
3507 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3508 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3509 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3510 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3511 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3512 i == EL_WALL_EMERALD ? EL_EMERALD :
3513 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3514 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3515 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3516 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3517 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3518 i == EL_WALL_PEARL ? EL_PEARL :
3519 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3524 /* ---------- initialize recursion detection ------------------------------ */
3525 recursion_loop_depth = 0;
3526 recursion_loop_detected = FALSE;
3527 recursion_loop_element = EL_UNDEFINED;
3529 /* ---------- initialize graphics engine ---------------------------------- */
3530 game.scroll_delay_value =
3531 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3532 setup.scroll_delay ? setup.scroll_delay_value : 0);
3533 game.scroll_delay_value =
3534 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3537 int get_num_special_action(int element, int action_first, int action_last)
3539 int num_special_action = 0;
3542 for (i = action_first; i <= action_last; i++)
3544 boolean found = FALSE;
3546 for (j = 0; j < NUM_DIRECTIONS; j++)
3547 if (el_act_dir2img(element, i, j) !=
3548 el_act_dir2img(element, ACTION_DEFAULT, j))
3552 num_special_action++;
3557 return num_special_action;
3562 =============================================================================
3564 -----------------------------------------------------------------------------
3565 initialize and start new game
3566 =============================================================================
3571 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3572 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3573 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3575 boolean do_fading = (game_status == GAME_MODE_MAIN);
3579 game_status = GAME_MODE_PLAYING;
3582 InitGameControlValues();
3584 /* don't play tapes over network */
3585 network_playing = (options.network && !tape.playing);
3587 for (i = 0; i < MAX_PLAYERS; i++)
3589 struct PlayerInfo *player = &stored_player[i];
3591 player->index_nr = i;
3592 player->index_bit = (1 << i);
3593 player->element_nr = EL_PLAYER_1 + i;
3595 player->present = FALSE;
3596 player->active = FALSE;
3597 player->killed = FALSE;
3600 player->effective_action = 0;
3601 player->programmed_action = 0;
3604 player->score_final = 0;
3606 player->gems_still_needed = level.gems_needed;
3607 player->sokobanfields_still_needed = 0;
3608 player->lights_still_needed = 0;
3609 player->friends_still_needed = 0;
3611 for (j = 0; j < MAX_NUM_KEYS; j++)
3612 player->key[j] = FALSE;
3614 player->num_white_keys = 0;
3616 player->dynabomb_count = 0;
3617 player->dynabomb_size = 1;
3618 player->dynabombs_left = 0;
3619 player->dynabomb_xl = FALSE;
3621 player->MovDir = MV_NONE;
3624 player->GfxDir = MV_NONE;
3625 player->GfxAction = ACTION_DEFAULT;
3627 player->StepFrame = 0;
3629 player->use_murphy = FALSE;
3630 player->artwork_element =
3631 (level.use_artwork_element[i] ? level.artwork_element[i] :
3632 player->element_nr);
3634 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3635 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3637 player->gravity = level.initial_player_gravity[i];
3639 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3641 player->actual_frame_counter = 0;
3643 player->step_counter = 0;
3645 player->last_move_dir = MV_NONE;
3647 player->is_active = FALSE;
3649 player->is_waiting = FALSE;
3650 player->is_moving = FALSE;
3651 player->is_auto_moving = FALSE;
3652 player->is_digging = FALSE;
3653 player->is_snapping = FALSE;
3654 player->is_collecting = FALSE;
3655 player->is_pushing = FALSE;
3656 player->is_switching = FALSE;
3657 player->is_dropping = FALSE;
3658 player->is_dropping_pressed = FALSE;
3660 player->is_bored = FALSE;
3661 player->is_sleeping = FALSE;
3663 player->frame_counter_bored = -1;
3664 player->frame_counter_sleeping = -1;
3666 player->anim_delay_counter = 0;
3667 player->post_delay_counter = 0;
3669 player->dir_waiting = MV_NONE;
3670 player->action_waiting = ACTION_DEFAULT;
3671 player->last_action_waiting = ACTION_DEFAULT;
3672 player->special_action_bored = ACTION_DEFAULT;
3673 player->special_action_sleeping = ACTION_DEFAULT;
3675 player->switch_x = -1;
3676 player->switch_y = -1;
3678 player->drop_x = -1;
3679 player->drop_y = -1;
3681 player->show_envelope = 0;
3683 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3685 player->push_delay = -1; /* initialized when pushing starts */
3686 player->push_delay_value = game.initial_push_delay_value;
3688 player->drop_delay = 0;
3689 player->drop_pressed_delay = 0;
3691 player->last_jx = -1;
3692 player->last_jy = -1;
3696 player->shield_normal_time_left = 0;
3697 player->shield_deadly_time_left = 0;
3699 player->inventory_infinite_element = EL_UNDEFINED;
3700 player->inventory_size = 0;
3702 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3703 SnapField(player, 0, 0);
3705 player->LevelSolved = FALSE;
3706 player->GameOver = FALSE;
3708 player->LevelSolved_GameWon = FALSE;
3709 player->LevelSolved_GameEnd = FALSE;
3710 player->LevelSolved_PanelOff = FALSE;
3711 player->LevelSolved_SaveTape = FALSE;
3712 player->LevelSolved_SaveScore = FALSE;
3713 player->LevelSolved_CountingTime = 0;
3714 player->LevelSolved_CountingScore = 0;
3717 network_player_action_received = FALSE;
3719 #if defined(NETWORK_AVALIABLE)
3720 /* initial null action */
3721 if (network_playing)
3722 SendToServer_MovePlayer(MV_NONE);
3731 TimeLeft = level.time;
3734 ScreenMovDir = MV_NONE;
3738 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3740 AllPlayersGone = FALSE;
3742 game.yamyam_content_nr = 0;
3743 game.robot_wheel_active = FALSE;
3744 game.magic_wall_active = FALSE;
3745 game.magic_wall_time_left = 0;
3746 game.light_time_left = 0;
3747 game.timegate_time_left = 0;
3748 game.switchgate_pos = 0;
3749 game.wind_direction = level.wind_direction_initial;
3751 #if !USE_PLAYER_GRAVITY
3752 game.gravity = FALSE;
3753 game.explosions_delayed = TRUE;
3756 game.lenses_time_left = 0;
3757 game.magnify_time_left = 0;
3759 game.ball_state = level.ball_state_initial;
3760 game.ball_content_nr = 0;
3762 game.envelope_active = FALSE;
3764 /* set focus to local player for network games, else to all players */
3765 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3766 game.centered_player_nr_next = game.centered_player_nr;
3767 game.set_centered_player = FALSE;
3769 if (network_playing && tape.recording)
3771 /* store client dependent player focus when recording network games */
3772 tape.centered_player_nr_next = game.centered_player_nr_next;
3773 tape.set_centered_player = TRUE;
3776 for (i = 0; i < NUM_BELTS; i++)
3778 game.belt_dir[i] = MV_NONE;
3779 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3782 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3783 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3785 SCAN_PLAYFIELD(x, y)
3787 Feld[x][y] = level.field[x][y];
3788 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3789 ChangeDelay[x][y] = 0;
3790 ChangePage[x][y] = -1;
3791 #if USE_NEW_CUSTOM_VALUE
3792 CustomValue[x][y] = 0; /* initialized in InitField() */
3794 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3796 WasJustMoving[x][y] = 0;
3797 WasJustFalling[x][y] = 0;
3798 CheckCollision[x][y] = 0;
3799 CheckImpact[x][y] = 0;
3801 Pushed[x][y] = FALSE;
3803 ChangeCount[x][y] = 0;
3804 ChangeEvent[x][y] = -1;
3806 ExplodePhase[x][y] = 0;
3807 ExplodeDelay[x][y] = 0;
3808 ExplodeField[x][y] = EX_TYPE_NONE;
3810 RunnerVisit[x][y] = 0;
3811 PlayerVisit[x][y] = 0;
3814 GfxRandom[x][y] = INIT_GFX_RANDOM();
3815 GfxElement[x][y] = EL_UNDEFINED;
3816 GfxAction[x][y] = ACTION_DEFAULT;
3817 GfxDir[x][y] = MV_NONE;
3820 SCAN_PLAYFIELD(x, y)
3822 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3824 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3826 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3829 InitField(x, y, TRUE);
3831 ResetGfxAnimation(x, y);
3836 for (i = 0; i < MAX_PLAYERS; i++)
3838 struct PlayerInfo *player = &stored_player[i];
3840 /* set number of special actions for bored and sleeping animation */
3841 player->num_special_action_bored =
3842 get_num_special_action(player->artwork_element,
3843 ACTION_BORING_1, ACTION_BORING_LAST);
3844 player->num_special_action_sleeping =
3845 get_num_special_action(player->artwork_element,
3846 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3849 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3850 emulate_sb ? EMU_SOKOBAN :
3851 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3853 #if USE_NEW_ALL_SLIPPERY
3854 /* initialize type of slippery elements */
3855 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3857 if (!IS_CUSTOM_ELEMENT(i))
3859 /* default: elements slip down either to the left or right randomly */
3860 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3862 /* SP style elements prefer to slip down on the left side */
3863 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3864 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3866 /* BD style elements prefer to slip down on the left side */
3867 if (game.emulation == EMU_BOULDERDASH)
3868 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3873 /* initialize explosion and ignition delay */
3874 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3876 if (!IS_CUSTOM_ELEMENT(i))
3879 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3880 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3881 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3882 int last_phase = (num_phase + 1) * delay;
3883 int half_phase = (num_phase / 2) * delay;
3885 element_info[i].explosion_delay = last_phase - 1;
3886 element_info[i].ignition_delay = half_phase;
3888 if (i == EL_BLACK_ORB)
3889 element_info[i].ignition_delay = 1;
3893 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
3894 element_info[i].explosion_delay = 1;
3896 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
3897 element_info[i].ignition_delay = 1;
3901 /* correct non-moving belts to start moving left */
3902 for (i = 0; i < NUM_BELTS; i++)
3903 if (game.belt_dir[i] == MV_NONE)
3904 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3906 /* check if any connected player was not found in playfield */
3907 for (i = 0; i < MAX_PLAYERS; i++)
3909 struct PlayerInfo *player = &stored_player[i];
3911 if (player->connected && !player->present)
3913 for (j = 0; j < MAX_PLAYERS; j++)
3915 struct PlayerInfo *some_player = &stored_player[j];
3916 int jx = some_player->jx, jy = some_player->jy;
3918 /* assign first free player found that is present in the playfield */
3919 if (some_player->present && !some_player->connected)
3921 player->present = TRUE;
3922 player->active = TRUE;
3924 some_player->present = FALSE;
3925 some_player->active = FALSE;
3927 player->artwork_element = some_player->artwork_element;
3929 player->block_last_field = some_player->block_last_field;
3930 player->block_delay_adjustment = some_player->block_delay_adjustment;
3932 StorePlayer[jx][jy] = player->element_nr;
3933 player->jx = player->last_jx = jx;
3934 player->jy = player->last_jy = jy;
3944 /* when playing a tape, eliminate all players who do not participate */
3946 for (i = 0; i < MAX_PLAYERS; i++)
3948 if (stored_player[i].active && !tape.player_participates[i])
3950 struct PlayerInfo *player = &stored_player[i];
3951 int jx = player->jx, jy = player->jy;
3953 player->active = FALSE;
3954 StorePlayer[jx][jy] = 0;
3955 Feld[jx][jy] = EL_EMPTY;
3959 else if (!options.network && !setup.team_mode) /* && !tape.playing */
3961 /* when in single player mode, eliminate all but the first active player */
3963 for (i = 0; i < MAX_PLAYERS; i++)
3965 if (stored_player[i].active)
3967 for (j = i + 1; j < MAX_PLAYERS; j++)
3969 if (stored_player[j].active)
3971 struct PlayerInfo *player = &stored_player[j];
3972 int jx = player->jx, jy = player->jy;
3974 player->active = FALSE;
3975 player->present = FALSE;
3977 StorePlayer[jx][jy] = 0;
3978 Feld[jx][jy] = EL_EMPTY;
3985 /* when recording the game, store which players take part in the game */
3988 for (i = 0; i < MAX_PLAYERS; i++)
3989 if (stored_player[i].active)
3990 tape.player_participates[i] = TRUE;
3995 for (i = 0; i < MAX_PLAYERS; i++)
3997 struct PlayerInfo *player = &stored_player[i];
3999 printf("Player %d: present == %d, connected == %d, active == %d.\n",
4004 if (local_player == player)
4005 printf("Player %d is local player.\n", i+1);
4009 if (BorderElement == EL_EMPTY)
4012 SBX_Right = lev_fieldx - SCR_FIELDX;
4014 SBY_Lower = lev_fieldy - SCR_FIELDY;
4019 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4021 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4024 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4025 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4027 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4028 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4030 /* if local player not found, look for custom element that might create
4031 the player (make some assumptions about the right custom element) */
4032 if (!local_player->present)
4034 int start_x = 0, start_y = 0;
4035 int found_rating = 0;
4036 int found_element = EL_UNDEFINED;
4037 int player_nr = local_player->index_nr;
4039 SCAN_PLAYFIELD(x, y)
4041 int element = Feld[x][y];
4046 if (level.use_start_element[player_nr] &&
4047 level.start_element[player_nr] == element &&
4054 found_element = element;
4057 if (!IS_CUSTOM_ELEMENT(element))
4060 if (CAN_CHANGE(element))
4062 for (i = 0; i < element_info[element].num_change_pages; i++)
4064 /* check for player created from custom element as single target */
4065 content = element_info[element].change_page[i].target_element;
4066 is_player = ELEM_IS_PLAYER(content);
4068 if (is_player && (found_rating < 3 ||
4069 (found_rating == 3 && element < found_element)))
4075 found_element = element;
4080 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4082 /* check for player created from custom element as explosion content */
4083 content = element_info[element].content.e[xx][yy];
4084 is_player = ELEM_IS_PLAYER(content);
4086 if (is_player && (found_rating < 2 ||
4087 (found_rating == 2 && element < found_element)))
4089 start_x = x + xx - 1;
4090 start_y = y + yy - 1;
4093 found_element = element;
4096 if (!CAN_CHANGE(element))
4099 for (i = 0; i < element_info[element].num_change_pages; i++)
4101 /* check for player created from custom element as extended target */
4103 element_info[element].change_page[i].target_content.e[xx][yy];
4105 is_player = ELEM_IS_PLAYER(content);
4107 if (is_player && (found_rating < 1 ||
4108 (found_rating == 1 && element < found_element)))
4110 start_x = x + xx - 1;
4111 start_y = y + yy - 1;
4114 found_element = element;
4120 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
4121 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4124 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4125 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4130 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
4131 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4132 local_player->jx - MIDPOSX);
4134 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4135 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4136 local_player->jy - MIDPOSY);
4140 /* do not use PLAYING mask for fading out from main screen */
4141 game_status = GAME_MODE_MAIN;
4146 if (!game.restart_level)
4147 CloseDoor(DOOR_CLOSE_1);
4150 if (level_editor_test_game)
4151 FadeSkipNextFadeIn();
4153 FadeSetEnterScreen();
4155 if (level_editor_test_game)
4156 fading = fading_none;
4158 fading = menu.destination;
4162 FadeOut(REDRAW_FIELD);
4165 FadeOut(REDRAW_FIELD);
4169 game_status = GAME_MODE_PLAYING;
4172 /* !!! FIX THIS (START) !!! */
4173 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4175 InitGameEngine_EM();
4177 /* blit playfield from scroll buffer to normal back buffer for fading in */
4178 BlitScreenToBitmap_EM(backbuffer);
4185 /* after drawing the level, correct some elements */
4186 if (game.timegate_time_left == 0)
4187 CloseAllOpenTimegates();
4189 /* blit playfield from scroll buffer to normal back buffer for fading in */
4190 if (setup.soft_scrolling)
4191 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4193 redraw_mask |= REDRAW_FROM_BACKBUFFER;
4195 /* !!! FIX THIS (END) !!! */
4198 FadeIn(REDRAW_FIELD);
4201 FadeIn(REDRAW_FIELD);
4206 if (!game.restart_level)
4208 /* copy default game door content to main double buffer */
4209 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4210 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4213 SetPanelBackground();
4214 SetDrawBackgroundMask(REDRAW_DOOR_1);
4216 UpdateGameDoorValues();
4217 DrawGameDoorValues();
4219 if (!game.restart_level)
4223 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4224 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4225 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4229 /* copy actual game door content to door double buffer for OpenDoor() */
4230 BlitBitmap(drawto, bitmap_db_door,
4231 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4233 OpenDoor(DOOR_OPEN_ALL);
4235 PlaySound(SND_GAME_STARTING);
4237 if (setup.sound_music)
4240 KeyboardAutoRepeatOffUnlessAutoplay();
4244 for (i = 0; i < MAX_PLAYERS; i++)
4245 printf("Player %d %sactive.\n",
4246 i + 1, (stored_player[i].active ? "" : "not "));
4257 game.restart_level = FALSE;
4260 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4262 /* this is used for non-R'n'D game engines to update certain engine values */
4264 /* needed to determine if sounds are played within the visible screen area */
4265 scroll_x = actual_scroll_x;
4266 scroll_y = actual_scroll_y;
4269 void InitMovDir(int x, int y)
4271 int i, element = Feld[x][y];
4272 static int xy[4][2] =
4279 static int direction[3][4] =
4281 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4282 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4283 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4292 Feld[x][y] = EL_BUG;
4293 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4296 case EL_SPACESHIP_RIGHT:
4297 case EL_SPACESHIP_UP:
4298 case EL_SPACESHIP_LEFT:
4299 case EL_SPACESHIP_DOWN:
4300 Feld[x][y] = EL_SPACESHIP;
4301 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4304 case EL_BD_BUTTERFLY_RIGHT:
4305 case EL_BD_BUTTERFLY_UP:
4306 case EL_BD_BUTTERFLY_LEFT:
4307 case EL_BD_BUTTERFLY_DOWN:
4308 Feld[x][y] = EL_BD_BUTTERFLY;
4309 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4312 case EL_BD_FIREFLY_RIGHT:
4313 case EL_BD_FIREFLY_UP:
4314 case EL_BD_FIREFLY_LEFT:
4315 case EL_BD_FIREFLY_DOWN:
4316 Feld[x][y] = EL_BD_FIREFLY;
4317 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4320 case EL_PACMAN_RIGHT:
4322 case EL_PACMAN_LEFT:
4323 case EL_PACMAN_DOWN:
4324 Feld[x][y] = EL_PACMAN;
4325 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4328 case EL_YAMYAM_LEFT:
4329 case EL_YAMYAM_RIGHT:
4331 case EL_YAMYAM_DOWN:
4332 Feld[x][y] = EL_YAMYAM;
4333 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4336 case EL_SP_SNIKSNAK:
4337 MovDir[x][y] = MV_UP;
4340 case EL_SP_ELECTRON:
4341 MovDir[x][y] = MV_LEFT;
4348 Feld[x][y] = EL_MOLE;
4349 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4353 if (IS_CUSTOM_ELEMENT(element))
4355 struct ElementInfo *ei = &element_info[element];
4356 int move_direction_initial = ei->move_direction_initial;
4357 int move_pattern = ei->move_pattern;
4359 if (move_direction_initial == MV_START_PREVIOUS)
4361 if (MovDir[x][y] != MV_NONE)
4364 move_direction_initial = MV_START_AUTOMATIC;
4367 if (move_direction_initial == MV_START_RANDOM)
4368 MovDir[x][y] = 1 << RND(4);
4369 else if (move_direction_initial & MV_ANY_DIRECTION)
4370 MovDir[x][y] = move_direction_initial;
4371 else if (move_pattern == MV_ALL_DIRECTIONS ||
4372 move_pattern == MV_TURNING_LEFT ||
4373 move_pattern == MV_TURNING_RIGHT ||
4374 move_pattern == MV_TURNING_LEFT_RIGHT ||
4375 move_pattern == MV_TURNING_RIGHT_LEFT ||
4376 move_pattern == MV_TURNING_RANDOM)
4377 MovDir[x][y] = 1 << RND(4);
4378 else if (move_pattern == MV_HORIZONTAL)
4379 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4380 else if (move_pattern == MV_VERTICAL)
4381 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4382 else if (move_pattern & MV_ANY_DIRECTION)
4383 MovDir[x][y] = element_info[element].move_pattern;
4384 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4385 move_pattern == MV_ALONG_RIGHT_SIDE)
4387 /* use random direction as default start direction */
4388 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4389 MovDir[x][y] = 1 << RND(4);
4391 for (i = 0; i < NUM_DIRECTIONS; i++)
4393 int x1 = x + xy[i][0];
4394 int y1 = y + xy[i][1];
4396 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4398 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4399 MovDir[x][y] = direction[0][i];
4401 MovDir[x][y] = direction[1][i];
4410 MovDir[x][y] = 1 << RND(4);
4412 if (element != EL_BUG &&
4413 element != EL_SPACESHIP &&
4414 element != EL_BD_BUTTERFLY &&
4415 element != EL_BD_FIREFLY)
4418 for (i = 0; i < NUM_DIRECTIONS; i++)
4420 int x1 = x + xy[i][0];
4421 int y1 = y + xy[i][1];
4423 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4425 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4427 MovDir[x][y] = direction[0][i];
4430 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4431 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4433 MovDir[x][y] = direction[1][i];
4442 GfxDir[x][y] = MovDir[x][y];
4445 void InitAmoebaNr(int x, int y)
4448 int group_nr = AmoebeNachbarNr(x, y);
4452 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4454 if (AmoebaCnt[i] == 0)
4462 AmoebaNr[x][y] = group_nr;
4463 AmoebaCnt[group_nr]++;
4464 AmoebaCnt2[group_nr]++;
4467 static void PlayerWins(struct PlayerInfo *player)
4469 player->LevelSolved = TRUE;
4470 player->GameOver = TRUE;
4472 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4473 level.native_em_level->lev->score : player->score);
4475 player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4476 player->LevelSolved_CountingScore = player->score_final;
4481 static int time, time_final;
4482 static int score, score_final;
4483 static int game_over_delay_1 = 0;
4484 static int game_over_delay_2 = 0;
4485 int game_over_delay_value_1 = 50;
4486 int game_over_delay_value_2 = 50;
4488 if (!local_player->LevelSolved_GameWon)
4492 /* do not start end game actions before the player stops moving (to exit) */
4493 if (local_player->MovPos)
4496 local_player->LevelSolved_GameWon = TRUE;
4497 local_player->LevelSolved_SaveTape = tape.recording;
4498 local_player->LevelSolved_SaveScore = !tape.playing;
4500 if (tape.auto_play) /* tape might already be stopped here */
4501 tape.auto_play_level_solved = TRUE;
4507 game_over_delay_1 = game_over_delay_value_1;
4508 game_over_delay_2 = game_over_delay_value_2;
4510 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4511 score = score_final = local_player->score_final;
4516 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4518 else if (level.time == 0 && TimePlayed < 999)
4521 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4524 local_player->score_final = score_final;
4526 if (level_editor_test_game)
4529 score = score_final;
4532 local_player->LevelSolved_CountingTime = time;
4533 local_player->LevelSolved_CountingScore = score;
4535 game_panel_controls[GAME_PANEL_TIME].value = time;
4536 game_panel_controls[GAME_PANEL_SCORE].value = score;
4538 DisplayGameControlValues();
4540 DrawGameValue_Time(time);
4541 DrawGameValue_Score(score);
4545 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4547 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4549 /* close exit door after last player */
4550 if ((AllPlayersGone &&
4551 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4552 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4553 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4554 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4555 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4557 int element = Feld[ExitX][ExitY];
4560 if (element == EL_EM_EXIT_OPEN ||
4561 element == EL_EM_STEEL_EXIT_OPEN)
4568 Feld[ExitX][ExitY] =
4569 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4570 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4571 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4572 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4573 EL_EM_STEEL_EXIT_CLOSING);
4575 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4579 /* player disappears */
4580 DrawLevelField(ExitX, ExitY);
4583 for (i = 0; i < MAX_PLAYERS; i++)
4585 struct PlayerInfo *player = &stored_player[i];
4587 if (player->present)
4589 RemovePlayer(player);
4591 /* player disappears */
4592 DrawLevelField(player->jx, player->jy);
4597 PlaySound(SND_GAME_WINNING);
4600 if (game_over_delay_1 > 0)
4602 game_over_delay_1--;
4607 if (time != time_final)
4609 int time_to_go = ABS(time_final - time);
4610 int time_count_dir = (time < time_final ? +1 : -1);
4611 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4613 time += time_count_steps * time_count_dir;
4614 score += time_count_steps * level.score[SC_TIME_BONUS];
4617 local_player->LevelSolved_CountingTime = time;
4618 local_player->LevelSolved_CountingScore = score;
4620 game_panel_controls[GAME_PANEL_TIME].value = time;
4621 game_panel_controls[GAME_PANEL_SCORE].value = score;
4623 DisplayGameControlValues();
4625 DrawGameValue_Time(time);
4626 DrawGameValue_Score(score);
4629 if (time == time_final)
4630 StopSound(SND_GAME_LEVELTIME_BONUS);
4631 else if (setup.sound_loops)
4632 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4634 PlaySound(SND_GAME_LEVELTIME_BONUS);
4639 local_player->LevelSolved_PanelOff = TRUE;
4641 if (game_over_delay_2 > 0)
4643 game_over_delay_2--;
4656 boolean raise_level = FALSE;
4658 local_player->LevelSolved_GameEnd = TRUE;
4660 CloseDoor(DOOR_CLOSE_1);
4662 if (local_player->LevelSolved_SaveTape)
4669 SaveTapeChecked(tape.level_nr); /* ask to save tape */
4671 SaveTape(tape.level_nr); /* ask to save tape */
4675 if (level_editor_test_game)
4677 game_status = GAME_MODE_MAIN;
4680 DrawAndFadeInMainMenu(REDRAW_FIELD);
4688 if (!local_player->LevelSolved_SaveScore)
4691 FadeOut(REDRAW_FIELD);
4694 game_status = GAME_MODE_MAIN;
4696 DrawAndFadeInMainMenu(REDRAW_FIELD);
4701 if (level_nr == leveldir_current->handicap_level)
4703 leveldir_current->handicap_level++;
4704 SaveLevelSetup_SeriesInfo();
4707 if (level_nr < leveldir_current->last_level)
4708 raise_level = TRUE; /* advance to next level */
4710 if ((hi_pos = NewHiScore()) >= 0)
4712 game_status = GAME_MODE_SCORES;
4714 DrawHallOfFame(hi_pos);
4725 FadeOut(REDRAW_FIELD);
4728 game_status = GAME_MODE_MAIN;
4736 DrawAndFadeInMainMenu(REDRAW_FIELD);
4745 LoadScore(level_nr);
4747 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4748 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4751 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4753 if (local_player->score_final > highscore[k].Score)
4755 /* player has made it to the hall of fame */
4757 if (k < MAX_SCORE_ENTRIES - 1)
4759 int m = MAX_SCORE_ENTRIES - 1;
4762 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4763 if (strEqual(setup.player_name, highscore[l].Name))
4765 if (m == k) /* player's new highscore overwrites his old one */
4769 for (l = m; l > k; l--)
4771 strcpy(highscore[l].Name, highscore[l - 1].Name);
4772 highscore[l].Score = highscore[l - 1].Score;
4779 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4780 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4781 highscore[k].Score = local_player->score_final;
4787 else if (!strncmp(setup.player_name, highscore[k].Name,
4788 MAX_PLAYER_NAME_LEN))
4789 break; /* player already there with a higher score */
4795 SaveScore(level_nr);
4800 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4802 int element = Feld[x][y];
4803 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4804 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4805 int horiz_move = (dx != 0);
4806 int sign = (horiz_move ? dx : dy);
4807 int step = sign * element_info[element].move_stepsize;
4809 /* special values for move stepsize for spring and things on conveyor belt */
4812 if (CAN_FALL(element) &&
4813 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4814 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4815 else if (element == EL_SPRING)
4816 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4822 inline static int getElementMoveStepsize(int x, int y)
4824 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4827 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4829 if (player->GfxAction != action || player->GfxDir != dir)
4832 printf("Player frame reset! (%d => %d, %d => %d)\n",
4833 player->GfxAction, action, player->GfxDir, dir);
4836 player->GfxAction = action;
4837 player->GfxDir = dir;
4839 player->StepFrame = 0;
4843 #if USE_GFX_RESET_GFX_ANIMATION
4844 static void ResetGfxFrame(int x, int y, boolean redraw)
4846 int element = Feld[x][y];
4847 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4848 int last_gfx_frame = GfxFrame[x][y];
4850 if (graphic_info[graphic].anim_global_sync)
4851 GfxFrame[x][y] = FrameCounter;
4852 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4853 GfxFrame[x][y] = CustomValue[x][y];
4854 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4855 GfxFrame[x][y] = element_info[element].collect_score;
4856 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4857 GfxFrame[x][y] = ChangeDelay[x][y];
4859 if (redraw && GfxFrame[x][y] != last_gfx_frame)
4860 DrawLevelGraphicAnimation(x, y, graphic);
4864 static void ResetGfxAnimation(int x, int y)
4866 GfxAction[x][y] = ACTION_DEFAULT;
4867 GfxDir[x][y] = MovDir[x][y];
4870 #if USE_GFX_RESET_GFX_ANIMATION
4871 ResetGfxFrame(x, y, FALSE);
4875 static void ResetRandomAnimationValue(int x, int y)
4877 GfxRandom[x][y] = INIT_GFX_RANDOM();
4880 void InitMovingField(int x, int y, int direction)
4882 int element = Feld[x][y];
4883 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4884 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4887 boolean is_moving_before, is_moving_after;
4889 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
4892 /* check if element was/is moving or being moved before/after mode change */
4895 is_moving_before = (WasJustMoving[x][y] != 0);
4897 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
4898 is_moving_before = WasJustMoving[x][y];
4901 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
4903 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
4905 /* reset animation only for moving elements which change direction of moving
4906 or which just started or stopped moving
4907 (else CEs with property "can move" / "not moving" are reset each frame) */
4908 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4910 if (is_moving_before != is_moving_after ||
4911 direction != MovDir[x][y])
4912 ResetGfxAnimation(x, y);
4914 if ((is_moving_before || is_moving_after) && !continues_moving)
4915 ResetGfxAnimation(x, y);
4918 if (!continues_moving)
4919 ResetGfxAnimation(x, y);
4922 MovDir[x][y] = direction;
4923 GfxDir[x][y] = direction;
4925 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4926 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4927 direction == MV_DOWN && CAN_FALL(element) ?
4928 ACTION_FALLING : ACTION_MOVING);
4930 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
4931 ACTION_FALLING : ACTION_MOVING);
4934 /* this is needed for CEs with property "can move" / "not moving" */
4936 if (is_moving_after)
4938 if (Feld[newx][newy] == EL_EMPTY)
4939 Feld[newx][newy] = EL_BLOCKED;
4941 MovDir[newx][newy] = MovDir[x][y];
4943 #if USE_NEW_CUSTOM_VALUE
4944 CustomValue[newx][newy] = CustomValue[x][y];
4947 GfxFrame[newx][newy] = GfxFrame[x][y];
4948 GfxRandom[newx][newy] = GfxRandom[x][y];
4949 GfxAction[newx][newy] = GfxAction[x][y];
4950 GfxDir[newx][newy] = GfxDir[x][y];
4954 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4956 int direction = MovDir[x][y];
4957 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4958 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
4964 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4966 int oldx = x, oldy = y;
4967 int direction = MovDir[x][y];
4969 if (direction == MV_LEFT)
4971 else if (direction == MV_RIGHT)
4973 else if (direction == MV_UP)
4975 else if (direction == MV_DOWN)
4978 *comes_from_x = oldx;
4979 *comes_from_y = oldy;
4982 int MovingOrBlocked2Element(int x, int y)
4984 int element = Feld[x][y];
4986 if (element == EL_BLOCKED)
4990 Blocked2Moving(x, y, &oldx, &oldy);
4991 return Feld[oldx][oldy];
4997 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4999 /* like MovingOrBlocked2Element(), but if element is moving
5000 and (x,y) is the field the moving element is just leaving,
5001 return EL_BLOCKED instead of the element value */
5002 int element = Feld[x][y];
5004 if (IS_MOVING(x, y))
5006 if (element == EL_BLOCKED)
5010 Blocked2Moving(x, y, &oldx, &oldy);
5011 return Feld[oldx][oldy];
5020 static void RemoveField(int x, int y)
5022 Feld[x][y] = EL_EMPTY;
5028 #if USE_NEW_CUSTOM_VALUE
5029 CustomValue[x][y] = 0;
5033 ChangeDelay[x][y] = 0;
5034 ChangePage[x][y] = -1;
5035 Pushed[x][y] = FALSE;
5038 ExplodeField[x][y] = EX_TYPE_NONE;
5041 GfxElement[x][y] = EL_UNDEFINED;
5042 GfxAction[x][y] = ACTION_DEFAULT;
5043 GfxDir[x][y] = MV_NONE;
5046 void RemoveMovingField(int x, int y)
5048 int oldx = x, oldy = y, newx = x, newy = y;
5049 int element = Feld[x][y];
5050 int next_element = EL_UNDEFINED;
5052 if (element != EL_BLOCKED && !IS_MOVING(x, y))
5055 if (IS_MOVING(x, y))
5057 Moving2Blocked(x, y, &newx, &newy);
5059 if (Feld[newx][newy] != EL_BLOCKED)
5061 /* element is moving, but target field is not free (blocked), but
5062 already occupied by something different (example: acid pool);
5063 in this case, only remove the moving field, but not the target */
5065 RemoveField(oldx, oldy);
5067 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5069 DrawLevelField(oldx, oldy);
5074 else if (element == EL_BLOCKED)
5076 Blocked2Moving(x, y, &oldx, &oldy);
5077 if (!IS_MOVING(oldx, oldy))
5081 if (element == EL_BLOCKED &&
5082 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5083 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5084 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5085 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5086 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5087 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5088 next_element = get_next_element(Feld[oldx][oldy]);
5090 RemoveField(oldx, oldy);
5091 RemoveField(newx, newy);
5093 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5095 if (next_element != EL_UNDEFINED)
5096 Feld[oldx][oldy] = next_element;
5098 DrawLevelField(oldx, oldy);
5099 DrawLevelField(newx, newy);
5102 void DrawDynamite(int x, int y)
5104 int sx = SCREENX(x), sy = SCREENY(y);
5105 int graphic = el2img(Feld[x][y]);
5108 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5111 if (IS_WALKABLE_INSIDE(Back[x][y]))
5115 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5116 else if (Store[x][y])
5117 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5119 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5121 if (Back[x][y] || Store[x][y])
5122 DrawGraphicThruMask(sx, sy, graphic, frame);
5124 DrawGraphic(sx, sy, graphic, frame);
5127 void CheckDynamite(int x, int y)
5129 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5133 if (MovDelay[x][y] != 0)
5136 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5142 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5147 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5149 boolean num_checked_players = 0;
5152 for (i = 0; i < MAX_PLAYERS; i++)
5154 if (stored_player[i].active)
5156 int sx = stored_player[i].jx;
5157 int sy = stored_player[i].jy;
5159 if (num_checked_players == 0)
5166 *sx1 = MIN(*sx1, sx);
5167 *sy1 = MIN(*sy1, sy);
5168 *sx2 = MAX(*sx2, sx);
5169 *sy2 = MAX(*sy2, sy);
5172 num_checked_players++;
5177 static boolean checkIfAllPlayersFitToScreen_RND()
5179 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5181 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5183 return (sx2 - sx1 < SCR_FIELDX &&
5184 sy2 - sy1 < SCR_FIELDY);
5187 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5189 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5191 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5193 *sx = (sx1 + sx2) / 2;
5194 *sy = (sy1 + sy2) / 2;
5197 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5198 boolean center_screen, boolean quick_relocation)
5200 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5201 boolean no_delay = (tape.warp_forward);
5202 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5203 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5205 if (quick_relocation)
5207 int offset = game.scroll_delay_value;
5209 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5211 if (!level.shifted_relocation || center_screen)
5213 /* quick relocation (without scrolling), with centering of screen */
5215 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5216 x > SBX_Right + MIDPOSX ? SBX_Right :
5219 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5220 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5225 /* quick relocation (without scrolling), but do not center screen */
5227 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5228 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5231 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5232 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5235 int offset_x = x + (scroll_x - center_scroll_x);
5236 int offset_y = y + (scroll_y - center_scroll_y);
5238 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5239 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5240 offset_x - MIDPOSX);
5242 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5243 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5244 offset_y - MIDPOSY);
5249 /* quick relocation (without scrolling), inside visible screen area */
5251 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
5252 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5253 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5255 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
5256 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5257 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5259 /* don't scroll over playfield boundaries */
5260 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5261 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5263 /* don't scroll over playfield boundaries */
5264 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5265 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5268 RedrawPlayfield(TRUE, 0,0,0,0);
5273 int scroll_xx, scroll_yy;
5275 if (!level.shifted_relocation || center_screen)
5277 /* visible relocation (with scrolling), with centering of screen */
5279 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5280 x > SBX_Right + MIDPOSX ? SBX_Right :
5283 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5284 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5289 /* visible relocation (with scrolling), but do not center screen */
5291 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5292 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5295 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5296 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5299 int offset_x = x + (scroll_x - center_scroll_x);
5300 int offset_y = y + (scroll_y - center_scroll_y);
5302 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5303 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5304 offset_x - MIDPOSX);
5306 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5307 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5308 offset_y - MIDPOSY);
5313 /* visible relocation (with scrolling), with centering of screen */
5315 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5316 x > SBX_Right + MIDPOSX ? SBX_Right :
5319 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5320 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5324 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5326 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5329 int fx = FX, fy = FY;
5331 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5332 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5334 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5340 fx += dx * TILEX / 2;
5341 fy += dy * TILEY / 2;
5343 ScrollLevel(dx, dy);
5346 /* scroll in two steps of half tile size to make things smoother */
5347 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5349 Delay(wait_delay_value);
5351 /* scroll second step to align at full tile size */
5353 Delay(wait_delay_value);
5358 Delay(wait_delay_value);
5362 void RelocatePlayer(int jx, int jy, int el_player_raw)
5364 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5365 int player_nr = GET_PLAYER_NR(el_player);
5366 struct PlayerInfo *player = &stored_player[player_nr];
5367 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5368 boolean no_delay = (tape.warp_forward);
5369 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5370 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5371 int old_jx = player->jx;
5372 int old_jy = player->jy;
5373 int old_element = Feld[old_jx][old_jy];
5374 int element = Feld[jx][jy];
5375 boolean player_relocated = (old_jx != jx || old_jy != jy);
5377 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5378 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5379 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5380 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5381 int leave_side_horiz = move_dir_horiz;
5382 int leave_side_vert = move_dir_vert;
5383 int enter_side = enter_side_horiz | enter_side_vert;
5384 int leave_side = leave_side_horiz | leave_side_vert;
5386 if (player->GameOver) /* do not reanimate dead player */
5389 if (!player_relocated) /* no need to relocate the player */
5392 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5394 RemoveField(jx, jy); /* temporarily remove newly placed player */
5395 DrawLevelField(jx, jy);
5398 if (player->present)
5400 while (player->MovPos)
5402 ScrollPlayer(player, SCROLL_GO_ON);
5403 ScrollScreen(NULL, SCROLL_GO_ON);
5405 AdvanceFrameAndPlayerCounters(player->index_nr);
5410 Delay(wait_delay_value);
5413 DrawPlayer(player); /* needed here only to cleanup last field */
5414 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5416 player->is_moving = FALSE;
5419 if (IS_CUSTOM_ELEMENT(old_element))
5420 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5422 player->index_bit, leave_side);
5424 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5426 player->index_bit, leave_side);
5428 Feld[jx][jy] = el_player;
5429 InitPlayerField(jx, jy, el_player, TRUE);
5431 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5433 Feld[jx][jy] = element;
5434 InitField(jx, jy, FALSE);
5437 /* only visually relocate centered player */
5438 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5439 FALSE, level.instant_relocation);
5441 TestIfPlayerTouchesBadThing(jx, jy);
5442 TestIfPlayerTouchesCustomElement(jx, jy);
5444 if (IS_CUSTOM_ELEMENT(element))
5445 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5446 player->index_bit, enter_side);
5448 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5449 player->index_bit, enter_side);
5452 void Explode(int ex, int ey, int phase, int mode)
5458 /* !!! eliminate this variable !!! */
5459 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5461 if (game.explosions_delayed)
5463 ExplodeField[ex][ey] = mode;
5467 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5469 int center_element = Feld[ex][ey];
5470 int artwork_element, explosion_element; /* set these values later */
5473 /* --- This is only really needed (and now handled) in "Impact()". --- */
5474 /* do not explode moving elements that left the explode field in time */
5475 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5476 center_element == EL_EMPTY &&
5477 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5482 /* !!! at this place, the center element may be EL_BLOCKED !!! */
5483 if (mode == EX_TYPE_NORMAL ||
5484 mode == EX_TYPE_CENTER ||
5485 mode == EX_TYPE_CROSS)
5486 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5489 /* remove things displayed in background while burning dynamite */
5490 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5493 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5495 /* put moving element to center field (and let it explode there) */
5496 center_element = MovingOrBlocked2Element(ex, ey);
5497 RemoveMovingField(ex, ey);
5498 Feld[ex][ey] = center_element;
5501 /* now "center_element" is finally determined -- set related values now */
5502 artwork_element = center_element; /* for custom player artwork */
5503 explosion_element = center_element; /* for custom player artwork */
5505 if (IS_PLAYER(ex, ey))
5507 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5509 artwork_element = stored_player[player_nr].artwork_element;
5511 if (level.use_explosion_element[player_nr])
5513 explosion_element = level.explosion_element[player_nr];
5514 artwork_element = explosion_element;
5519 if (mode == EX_TYPE_NORMAL ||
5520 mode == EX_TYPE_CENTER ||
5521 mode == EX_TYPE_CROSS)
5522 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5525 last_phase = element_info[explosion_element].explosion_delay + 1;
5527 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5529 int xx = x - ex + 1;
5530 int yy = y - ey + 1;
5533 if (!IN_LEV_FIELD(x, y) ||
5534 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5535 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5538 element = Feld[x][y];
5540 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5542 element = MovingOrBlocked2Element(x, y);
5544 if (!IS_EXPLOSION_PROOF(element))
5545 RemoveMovingField(x, y);
5548 /* indestructible elements can only explode in center (but not flames) */
5549 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5550 mode == EX_TYPE_BORDER)) ||
5551 element == EL_FLAMES)
5554 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5555 behaviour, for example when touching a yamyam that explodes to rocks
5556 with active deadly shield, a rock is created under the player !!! */
5557 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5559 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5560 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5561 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5563 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5566 if (IS_ACTIVE_BOMB(element))
5568 /* re-activate things under the bomb like gate or penguin */
5569 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5576 /* save walkable background elements while explosion on same tile */
5577 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5578 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5579 Back[x][y] = element;
5581 /* ignite explodable elements reached by other explosion */
5582 if (element == EL_EXPLOSION)
5583 element = Store2[x][y];
5585 if (AmoebaNr[x][y] &&
5586 (element == EL_AMOEBA_FULL ||
5587 element == EL_BD_AMOEBA ||
5588 element == EL_AMOEBA_GROWING))
5590 AmoebaCnt[AmoebaNr[x][y]]--;
5591 AmoebaCnt2[AmoebaNr[x][y]]--;
5596 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5598 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5600 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5602 if (PLAYERINFO(ex, ey)->use_murphy)
5603 Store[x][y] = EL_EMPTY;
5606 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5607 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5608 else if (ELEM_IS_PLAYER(center_element))
5609 Store[x][y] = EL_EMPTY;
5610 else if (center_element == EL_YAMYAM)
5611 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5612 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5613 Store[x][y] = element_info[center_element].content.e[xx][yy];
5615 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5616 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5617 otherwise) -- FIX THIS !!! */
5618 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5619 Store[x][y] = element_info[element].content.e[1][1];
5621 else if (!CAN_EXPLODE(element))
5622 Store[x][y] = element_info[element].content.e[1][1];
5625 Store[x][y] = EL_EMPTY;
5627 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5628 center_element == EL_AMOEBA_TO_DIAMOND)
5629 Store2[x][y] = element;
5631 Feld[x][y] = EL_EXPLOSION;
5632 GfxElement[x][y] = artwork_element;
5634 ExplodePhase[x][y] = 1;
5635 ExplodeDelay[x][y] = last_phase;
5640 if (center_element == EL_YAMYAM)
5641 game.yamyam_content_nr =
5642 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5654 GfxFrame[x][y] = 0; /* restart explosion animation */
5656 last_phase = ExplodeDelay[x][y];
5658 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5662 /* activate this even in non-DEBUG version until cause for crash in
5663 getGraphicAnimationFrame() (see below) is found and eliminated */
5669 /* this can happen if the player leaves an explosion just in time */
5670 if (GfxElement[x][y] == EL_UNDEFINED)
5671 GfxElement[x][y] = EL_EMPTY;
5673 if (GfxElement[x][y] == EL_UNDEFINED)
5676 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5677 printf("Explode(): This should never happen!\n");
5680 GfxElement[x][y] = EL_EMPTY;
5686 border_element = Store2[x][y];
5687 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5688 border_element = StorePlayer[x][y];
5690 if (phase == element_info[border_element].ignition_delay ||
5691 phase == last_phase)
5693 boolean border_explosion = FALSE;
5695 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5696 !PLAYER_EXPLOSION_PROTECTED(x, y))
5698 KillPlayerUnlessExplosionProtected(x, y);
5699 border_explosion = TRUE;
5701 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5703 Feld[x][y] = Store2[x][y];
5706 border_explosion = TRUE;
5708 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5710 AmoebeUmwandeln(x, y);
5712 border_explosion = TRUE;
5715 /* if an element just explodes due to another explosion (chain-reaction),
5716 do not immediately end the new explosion when it was the last frame of
5717 the explosion (as it would be done in the following "if"-statement!) */
5718 if (border_explosion && phase == last_phase)
5722 if (phase == last_phase)
5726 element = Feld[x][y] = Store[x][y];
5727 Store[x][y] = Store2[x][y] = 0;
5728 GfxElement[x][y] = EL_UNDEFINED;
5730 /* player can escape from explosions and might therefore be still alive */
5731 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5732 element <= EL_PLAYER_IS_EXPLODING_4)
5734 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5735 int explosion_element = EL_PLAYER_1 + player_nr;
5736 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5737 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5739 if (level.use_explosion_element[player_nr])
5740 explosion_element = level.explosion_element[player_nr];
5742 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5743 element_info[explosion_element].content.e[xx][yy]);
5746 /* restore probably existing indestructible background element */
5747 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5748 element = Feld[x][y] = Back[x][y];
5751 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5752 GfxDir[x][y] = MV_NONE;
5753 ChangeDelay[x][y] = 0;
5754 ChangePage[x][y] = -1;
5756 #if USE_NEW_CUSTOM_VALUE
5757 CustomValue[x][y] = 0;
5760 InitField_WithBug2(x, y, FALSE);
5762 DrawLevelField(x, y);
5764 TestIfElementTouchesCustomElement(x, y);
5766 if (GFX_CRUMBLED(element))
5767 DrawLevelFieldCrumbledSandNeighbours(x, y);
5769 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5770 StorePlayer[x][y] = 0;
5772 if (ELEM_IS_PLAYER(element))
5773 RelocatePlayer(x, y, element);
5775 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5777 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5778 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5781 DrawLevelFieldCrumbledSand(x, y);
5783 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5785 DrawLevelElement(x, y, Back[x][y]);
5786 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5788 else if (IS_WALKABLE_UNDER(Back[x][y]))
5790 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5791 DrawLevelElementThruMask(x, y, Back[x][y]);
5793 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5794 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5798 void DynaExplode(int ex, int ey)
5801 int dynabomb_element = Feld[ex][ey];
5802 int dynabomb_size = 1;
5803 boolean dynabomb_xl = FALSE;
5804 struct PlayerInfo *player;
5805 static int xy[4][2] =
5813 if (IS_ACTIVE_BOMB(dynabomb_element))
5815 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5816 dynabomb_size = player->dynabomb_size;
5817 dynabomb_xl = player->dynabomb_xl;
5818 player->dynabombs_left++;
5821 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5823 for (i = 0; i < NUM_DIRECTIONS; i++)
5825 for (j = 1; j <= dynabomb_size; j++)
5827 int x = ex + j * xy[i][0];
5828 int y = ey + j * xy[i][1];
5831 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5834 element = Feld[x][y];
5836 /* do not restart explosions of fields with active bombs */
5837 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5840 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5842 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5843 !IS_DIGGABLE(element) && !dynabomb_xl)
5849 void Bang(int x, int y)
5851 int element = MovingOrBlocked2Element(x, y);
5852 int explosion_type = EX_TYPE_NORMAL;
5854 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5856 struct PlayerInfo *player = PLAYERINFO(x, y);
5858 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
5859 player->element_nr);
5861 if (level.use_explosion_element[player->index_nr])
5863 int explosion_element = level.explosion_element[player->index_nr];
5865 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5866 explosion_type = EX_TYPE_CROSS;
5867 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5868 explosion_type = EX_TYPE_CENTER;
5876 case EL_BD_BUTTERFLY:
5879 case EL_DARK_YAMYAM:
5883 RaiseScoreElement(element);
5886 case EL_DYNABOMB_PLAYER_1_ACTIVE:
5887 case EL_DYNABOMB_PLAYER_2_ACTIVE:
5888 case EL_DYNABOMB_PLAYER_3_ACTIVE:
5889 case EL_DYNABOMB_PLAYER_4_ACTIVE:
5890 case EL_DYNABOMB_INCREASE_NUMBER:
5891 case EL_DYNABOMB_INCREASE_SIZE:
5892 case EL_DYNABOMB_INCREASE_POWER:
5893 explosion_type = EX_TYPE_DYNA;
5896 case EL_DC_LANDMINE:
5898 case EL_EM_EXIT_OPEN:
5899 case EL_EM_STEEL_EXIT_OPEN:
5901 explosion_type = EX_TYPE_CENTER;
5906 case EL_LAMP_ACTIVE:
5907 case EL_AMOEBA_TO_DIAMOND:
5908 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
5909 explosion_type = EX_TYPE_CENTER;
5913 if (element_info[element].explosion_type == EXPLODES_CROSS)
5914 explosion_type = EX_TYPE_CROSS;
5915 else if (element_info[element].explosion_type == EXPLODES_1X1)
5916 explosion_type = EX_TYPE_CENTER;
5920 if (explosion_type == EX_TYPE_DYNA)
5923 Explode(x, y, EX_PHASE_START, explosion_type);
5925 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5928 void SplashAcid(int x, int y)
5930 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5931 (!IN_LEV_FIELD(x - 1, y - 2) ||
5932 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5933 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5935 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5936 (!IN_LEV_FIELD(x + 1, y - 2) ||
5937 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5938 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5940 PlayLevelSound(x, y, SND_ACID_SPLASHING);
5943 static void InitBeltMovement()
5945 static int belt_base_element[4] =
5947 EL_CONVEYOR_BELT_1_LEFT,
5948 EL_CONVEYOR_BELT_2_LEFT,
5949 EL_CONVEYOR_BELT_3_LEFT,
5950 EL_CONVEYOR_BELT_4_LEFT
5952 static int belt_base_active_element[4] =
5954 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5955 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5956 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5957 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5962 /* set frame order for belt animation graphic according to belt direction */
5963 for (i = 0; i < NUM_BELTS; i++)
5967 for (j = 0; j < NUM_BELT_PARTS; j++)
5969 int element = belt_base_active_element[belt_nr] + j;
5970 int graphic_1 = el2img(element);
5971 int graphic_2 = el2panelimg(element);
5973 if (game.belt_dir[i] == MV_LEFT)
5975 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5976 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5980 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5981 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5986 SCAN_PLAYFIELD(x, y)
5988 int element = Feld[x][y];
5990 for (i = 0; i < NUM_BELTS; i++)
5992 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5994 int e_belt_nr = getBeltNrFromBeltElement(element);
5997 if (e_belt_nr == belt_nr)
5999 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6001 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6008 static void ToggleBeltSwitch(int x, int y)
6010 static int belt_base_element[4] =
6012 EL_CONVEYOR_BELT_1_LEFT,
6013 EL_CONVEYOR_BELT_2_LEFT,
6014 EL_CONVEYOR_BELT_3_LEFT,
6015 EL_CONVEYOR_BELT_4_LEFT
6017 static int belt_base_active_element[4] =
6019 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6020 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6021 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6022 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6024 static int belt_base_switch_element[4] =
6026 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6027 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6028 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6029 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6031 static int belt_move_dir[4] =
6039 int element = Feld[x][y];
6040 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6041 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6042 int belt_dir = belt_move_dir[belt_dir_nr];
6045 if (!IS_BELT_SWITCH(element))
6048 game.belt_dir_nr[belt_nr] = belt_dir_nr;
6049 game.belt_dir[belt_nr] = belt_dir;
6051 if (belt_dir_nr == 3)
6054 /* set frame order for belt animation graphic according to belt direction */
6055 for (i = 0; i < NUM_BELT_PARTS; i++)
6057 int element = belt_base_active_element[belt_nr] + i;
6058 int graphic_1 = el2img(element);
6059 int graphic_2 = el2panelimg(element);
6061 if (belt_dir == MV_LEFT)
6063 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6064 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6068 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6069 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6073 SCAN_PLAYFIELD(xx, yy)
6075 int element = Feld[xx][yy];
6077 if (IS_BELT_SWITCH(element))
6079 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6081 if (e_belt_nr == belt_nr)
6083 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6084 DrawLevelField(xx, yy);
6087 else if (IS_BELT(element) && belt_dir != MV_NONE)
6089 int e_belt_nr = getBeltNrFromBeltElement(element);
6091 if (e_belt_nr == belt_nr)
6093 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6095 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6096 DrawLevelField(xx, yy);
6099 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6101 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6103 if (e_belt_nr == belt_nr)
6105 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6107 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6108 DrawLevelField(xx, yy);
6114 static void ToggleSwitchgateSwitch(int x, int y)
6118 game.switchgate_pos = !game.switchgate_pos;
6120 SCAN_PLAYFIELD(xx, yy)
6122 int element = Feld[xx][yy];
6124 #if !USE_BOTH_SWITCHGATE_SWITCHES
6125 if (element == EL_SWITCHGATE_SWITCH_UP ||
6126 element == EL_SWITCHGATE_SWITCH_DOWN)
6128 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6129 DrawLevelField(xx, yy);
6131 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6132 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6134 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6135 DrawLevelField(xx, yy);
6138 if (element == EL_SWITCHGATE_SWITCH_UP)
6140 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6141 DrawLevelField(xx, yy);
6143 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6145 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6146 DrawLevelField(xx, yy);
6148 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6150 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6151 DrawLevelField(xx, yy);
6153 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6155 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6156 DrawLevelField(xx, yy);
6159 else if (element == EL_SWITCHGATE_OPEN ||
6160 element == EL_SWITCHGATE_OPENING)
6162 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6164 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6166 else if (element == EL_SWITCHGATE_CLOSED ||
6167 element == EL_SWITCHGATE_CLOSING)
6169 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6171 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6176 static int getInvisibleActiveFromInvisibleElement(int element)
6178 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6179 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6180 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6184 static int getInvisibleFromInvisibleActiveElement(int element)
6186 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6187 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6188 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6192 static void RedrawAllLightSwitchesAndInvisibleElements()
6196 SCAN_PLAYFIELD(x, y)
6198 int element = Feld[x][y];
6200 if (element == EL_LIGHT_SWITCH &&
6201 game.light_time_left > 0)
6203 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6204 DrawLevelField(x, y);
6206 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6207 game.light_time_left == 0)
6209 Feld[x][y] = EL_LIGHT_SWITCH;
6210 DrawLevelField(x, y);
6212 else if (element == EL_EMC_DRIPPER &&
6213 game.light_time_left > 0)
6215 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6216 DrawLevelField(x, y);
6218 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6219 game.light_time_left == 0)
6221 Feld[x][y] = EL_EMC_DRIPPER;
6222 DrawLevelField(x, y);
6224 else if (element == EL_INVISIBLE_STEELWALL ||
6225 element == EL_INVISIBLE_WALL ||
6226 element == EL_INVISIBLE_SAND)
6228 if (game.light_time_left > 0)
6229 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6231 DrawLevelField(x, y);
6233 /* uncrumble neighbour fields, if needed */
6234 if (element == EL_INVISIBLE_SAND)
6235 DrawLevelFieldCrumbledSandNeighbours(x, y);
6237 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6238 element == EL_INVISIBLE_WALL_ACTIVE ||
6239 element == EL_INVISIBLE_SAND_ACTIVE)
6241 if (game.light_time_left == 0)
6242 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6244 DrawLevelField(x, y);
6246 /* re-crumble neighbour fields, if needed */
6247 if (element == EL_INVISIBLE_SAND)
6248 DrawLevelFieldCrumbledSandNeighbours(x, y);
6253 static void RedrawAllInvisibleElementsForLenses()
6257 SCAN_PLAYFIELD(x, y)
6259 int element = Feld[x][y];
6261 if (element == EL_EMC_DRIPPER &&
6262 game.lenses_time_left > 0)
6264 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6265 DrawLevelField(x, y);
6267 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6268 game.lenses_time_left == 0)
6270 Feld[x][y] = EL_EMC_DRIPPER;
6271 DrawLevelField(x, y);
6273 else if (element == EL_INVISIBLE_STEELWALL ||
6274 element == EL_INVISIBLE_WALL ||
6275 element == EL_INVISIBLE_SAND)
6277 if (game.lenses_time_left > 0)
6278 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6280 DrawLevelField(x, y);
6282 /* uncrumble neighbour fields, if needed */
6283 if (element == EL_INVISIBLE_SAND)
6284 DrawLevelFieldCrumbledSandNeighbours(x, y);
6286 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6287 element == EL_INVISIBLE_WALL_ACTIVE ||
6288 element == EL_INVISIBLE_SAND_ACTIVE)
6290 if (game.lenses_time_left == 0)
6291 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6293 DrawLevelField(x, y);
6295 /* re-crumble neighbour fields, if needed */
6296 if (element == EL_INVISIBLE_SAND)
6297 DrawLevelFieldCrumbledSandNeighbours(x, y);
6302 static void RedrawAllInvisibleElementsForMagnifier()
6306 SCAN_PLAYFIELD(x, y)
6308 int element = Feld[x][y];
6310 if (element == EL_EMC_FAKE_GRASS &&
6311 game.magnify_time_left > 0)
6313 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6314 DrawLevelField(x, y);
6316 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6317 game.magnify_time_left == 0)
6319 Feld[x][y] = EL_EMC_FAKE_GRASS;
6320 DrawLevelField(x, y);
6322 else if (IS_GATE_GRAY(element) &&
6323 game.magnify_time_left > 0)
6325 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6326 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6327 IS_EM_GATE_GRAY(element) ?
6328 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6329 IS_EMC_GATE_GRAY(element) ?
6330 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6332 DrawLevelField(x, y);
6334 else if (IS_GATE_GRAY_ACTIVE(element) &&
6335 game.magnify_time_left == 0)
6337 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6338 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6339 IS_EM_GATE_GRAY_ACTIVE(element) ?
6340 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6341 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6342 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6344 DrawLevelField(x, y);
6349 static void ToggleLightSwitch(int x, int y)
6351 int element = Feld[x][y];
6353 game.light_time_left =
6354 (element == EL_LIGHT_SWITCH ?
6355 level.time_light * FRAMES_PER_SECOND : 0);
6357 RedrawAllLightSwitchesAndInvisibleElements();
6360 static void ActivateTimegateSwitch(int x, int y)
6364 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6366 SCAN_PLAYFIELD(xx, yy)
6368 int element = Feld[xx][yy];
6370 if (element == EL_TIMEGATE_CLOSED ||
6371 element == EL_TIMEGATE_CLOSING)
6373 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6374 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6378 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6380 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6381 DrawLevelField(xx, yy);
6388 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6389 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6391 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6395 void Impact(int x, int y)
6397 boolean last_line = (y == lev_fieldy - 1);
6398 boolean object_hit = FALSE;
6399 boolean impact = (last_line || object_hit);
6400 int element = Feld[x][y];
6401 int smashed = EL_STEELWALL;
6403 if (!last_line) /* check if element below was hit */
6405 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6408 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6409 MovDir[x][y + 1] != MV_DOWN ||
6410 MovPos[x][y + 1] <= TILEY / 2));
6412 /* do not smash moving elements that left the smashed field in time */
6413 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6414 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6417 #if USE_QUICKSAND_IMPACT_BUGFIX
6418 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6420 RemoveMovingField(x, y + 1);
6421 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6422 Feld[x][y + 2] = EL_ROCK;
6423 DrawLevelField(x, y + 2);
6428 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6430 RemoveMovingField(x, y + 1);
6431 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6432 Feld[x][y + 2] = EL_ROCK;
6433 DrawLevelField(x, y + 2);
6440 smashed = MovingOrBlocked2Element(x, y + 1);
6442 impact = (last_line || object_hit);
6445 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6447 SplashAcid(x, y + 1);
6451 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6452 /* only reset graphic animation if graphic really changes after impact */
6454 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6456 ResetGfxAnimation(x, y);
6457 DrawLevelField(x, y);
6460 if (impact && CAN_EXPLODE_IMPACT(element))
6465 else if (impact && element == EL_PEARL &&
6466 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6468 ResetGfxAnimation(x, y);
6470 Feld[x][y] = EL_PEARL_BREAKING;
6471 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6474 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6476 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6481 if (impact && element == EL_AMOEBA_DROP)
6483 if (object_hit && IS_PLAYER(x, y + 1))
6484 KillPlayerUnlessEnemyProtected(x, y + 1);
6485 else if (object_hit && smashed == EL_PENGUIN)
6489 Feld[x][y] = EL_AMOEBA_GROWING;
6490 Store[x][y] = EL_AMOEBA_WET;
6492 ResetRandomAnimationValue(x, y);
6497 if (object_hit) /* check which object was hit */
6499 if ((CAN_PASS_MAGIC_WALL(element) &&
6500 (smashed == EL_MAGIC_WALL ||
6501 smashed == EL_BD_MAGIC_WALL)) ||
6502 (CAN_PASS_DC_MAGIC_WALL(element) &&
6503 smashed == EL_DC_MAGIC_WALL))
6506 int activated_magic_wall =
6507 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6508 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6509 EL_DC_MAGIC_WALL_ACTIVE);
6511 /* activate magic wall / mill */
6512 SCAN_PLAYFIELD(xx, yy)
6514 if (Feld[xx][yy] == smashed)
6515 Feld[xx][yy] = activated_magic_wall;
6518 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6519 game.magic_wall_active = TRUE;
6521 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6522 SND_MAGIC_WALL_ACTIVATING :
6523 smashed == EL_BD_MAGIC_WALL ?
6524 SND_BD_MAGIC_WALL_ACTIVATING :
6525 SND_DC_MAGIC_WALL_ACTIVATING));
6528 if (IS_PLAYER(x, y + 1))
6530 if (CAN_SMASH_PLAYER(element))
6532 KillPlayerUnlessEnemyProtected(x, y + 1);
6536 else if (smashed == EL_PENGUIN)
6538 if (CAN_SMASH_PLAYER(element))
6544 else if (element == EL_BD_DIAMOND)
6546 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6552 else if (((element == EL_SP_INFOTRON ||
6553 element == EL_SP_ZONK) &&
6554 (smashed == EL_SP_SNIKSNAK ||
6555 smashed == EL_SP_ELECTRON ||
6556 smashed == EL_SP_DISK_ORANGE)) ||
6557 (element == EL_SP_INFOTRON &&
6558 smashed == EL_SP_DISK_YELLOW))
6563 else if (CAN_SMASH_EVERYTHING(element))
6565 if (IS_CLASSIC_ENEMY(smashed) ||
6566 CAN_EXPLODE_SMASHED(smashed))
6571 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6573 if (smashed == EL_LAMP ||
6574 smashed == EL_LAMP_ACTIVE)
6579 else if (smashed == EL_NUT)
6581 Feld[x][y + 1] = EL_NUT_BREAKING;
6582 PlayLevelSound(x, y, SND_NUT_BREAKING);
6583 RaiseScoreElement(EL_NUT);
6586 else if (smashed == EL_PEARL)
6588 ResetGfxAnimation(x, y);
6590 Feld[x][y + 1] = EL_PEARL_BREAKING;
6591 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6594 else if (smashed == EL_DIAMOND)
6596 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6597 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6600 else if (IS_BELT_SWITCH(smashed))
6602 ToggleBeltSwitch(x, y + 1);
6604 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6605 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6606 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6607 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6609 ToggleSwitchgateSwitch(x, y + 1);
6611 else if (smashed == EL_LIGHT_SWITCH ||
6612 smashed == EL_LIGHT_SWITCH_ACTIVE)
6614 ToggleLightSwitch(x, y + 1);
6619 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6622 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6624 CheckElementChangeBySide(x, y + 1, smashed, element,
6625 CE_SWITCHED, CH_SIDE_TOP);
6626 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6632 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6637 /* play sound of magic wall / mill */
6639 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6640 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6641 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6643 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6644 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6645 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6646 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6647 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6648 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6653 /* play sound of object that hits the ground */
6654 if (last_line || object_hit)
6655 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6658 inline static void TurnRoundExt(int x, int y)
6670 { 0, 0 }, { 0, 0 }, { 0, 0 },
6675 int left, right, back;
6679 { MV_DOWN, MV_UP, MV_RIGHT },
6680 { MV_UP, MV_DOWN, MV_LEFT },
6682 { MV_LEFT, MV_RIGHT, MV_DOWN },
6686 { MV_RIGHT, MV_LEFT, MV_UP }
6689 int element = Feld[x][y];
6690 int move_pattern = element_info[element].move_pattern;
6692 int old_move_dir = MovDir[x][y];
6693 int left_dir = turn[old_move_dir].left;
6694 int right_dir = turn[old_move_dir].right;
6695 int back_dir = turn[old_move_dir].back;
6697 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6698 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6699 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6700 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6702 int left_x = x + left_dx, left_y = y + left_dy;
6703 int right_x = x + right_dx, right_y = y + right_dy;
6704 int move_x = x + move_dx, move_y = y + move_dy;
6708 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6710 TestIfBadThingTouchesOtherBadThing(x, y);
6712 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6713 MovDir[x][y] = right_dir;
6714 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6715 MovDir[x][y] = left_dir;
6717 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6719 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6722 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6724 TestIfBadThingTouchesOtherBadThing(x, y);
6726 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6727 MovDir[x][y] = left_dir;
6728 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6729 MovDir[x][y] = right_dir;
6731 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6733 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6736 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6738 TestIfBadThingTouchesOtherBadThing(x, y);
6740 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6741 MovDir[x][y] = left_dir;
6742 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6743 MovDir[x][y] = right_dir;
6745 if (MovDir[x][y] != old_move_dir)
6748 else if (element == EL_YAMYAM)
6750 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6751 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6753 if (can_turn_left && can_turn_right)
6754 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6755 else if (can_turn_left)
6756 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6757 else if (can_turn_right)
6758 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6760 MovDir[x][y] = back_dir;
6762 MovDelay[x][y] = 16 + 16 * RND(3);
6764 else if (element == EL_DARK_YAMYAM)
6766 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6768 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6771 if (can_turn_left && can_turn_right)
6772 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6773 else if (can_turn_left)
6774 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6775 else if (can_turn_right)
6776 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6778 MovDir[x][y] = back_dir;
6780 MovDelay[x][y] = 16 + 16 * RND(3);
6782 else if (element == EL_PACMAN)
6784 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6785 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6787 if (can_turn_left && can_turn_right)
6788 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6789 else if (can_turn_left)
6790 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6791 else if (can_turn_right)
6792 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6794 MovDir[x][y] = back_dir;
6796 MovDelay[x][y] = 6 + RND(40);
6798 else if (element == EL_PIG)
6800 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6801 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6802 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6803 boolean should_turn_left, should_turn_right, should_move_on;
6805 int rnd = RND(rnd_value);
6807 should_turn_left = (can_turn_left &&
6809 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6810 y + back_dy + left_dy)));
6811 should_turn_right = (can_turn_right &&
6813 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6814 y + back_dy + right_dy)));
6815 should_move_on = (can_move_on &&
6818 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6819 y + move_dy + left_dy) ||
6820 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6821 y + move_dy + right_dy)));
6823 if (should_turn_left || should_turn_right || should_move_on)
6825 if (should_turn_left && should_turn_right && should_move_on)
6826 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6827 rnd < 2 * rnd_value / 3 ? right_dir :
6829 else if (should_turn_left && should_turn_right)
6830 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6831 else if (should_turn_left && should_move_on)
6832 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6833 else if (should_turn_right && should_move_on)
6834 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6835 else if (should_turn_left)
6836 MovDir[x][y] = left_dir;
6837 else if (should_turn_right)
6838 MovDir[x][y] = right_dir;
6839 else if (should_move_on)
6840 MovDir[x][y] = old_move_dir;
6842 else if (can_move_on && rnd > rnd_value / 8)
6843 MovDir[x][y] = old_move_dir;
6844 else if (can_turn_left && can_turn_right)
6845 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6846 else if (can_turn_left && rnd > rnd_value / 8)
6847 MovDir[x][y] = left_dir;
6848 else if (can_turn_right && rnd > rnd_value/8)
6849 MovDir[x][y] = right_dir;
6851 MovDir[x][y] = back_dir;
6853 xx = x + move_xy[MovDir[x][y]].dx;
6854 yy = y + move_xy[MovDir[x][y]].dy;
6856 if (!IN_LEV_FIELD(xx, yy) ||
6857 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6858 MovDir[x][y] = old_move_dir;
6862 else if (element == EL_DRAGON)
6864 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6865 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6866 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6868 int rnd = RND(rnd_value);
6870 if (can_move_on && rnd > rnd_value / 8)
6871 MovDir[x][y] = old_move_dir;
6872 else if (can_turn_left && can_turn_right)
6873 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6874 else if (can_turn_left && rnd > rnd_value / 8)
6875 MovDir[x][y] = left_dir;
6876 else if (can_turn_right && rnd > rnd_value / 8)
6877 MovDir[x][y] = right_dir;
6879 MovDir[x][y] = back_dir;
6881 xx = x + move_xy[MovDir[x][y]].dx;
6882 yy = y + move_xy[MovDir[x][y]].dy;
6884 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6885 MovDir[x][y] = old_move_dir;
6889 else if (element == EL_MOLE)
6891 boolean can_move_on =
6892 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6893 IS_AMOEBOID(Feld[move_x][move_y]) ||
6894 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6897 boolean can_turn_left =
6898 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6899 IS_AMOEBOID(Feld[left_x][left_y])));
6901 boolean can_turn_right =
6902 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6903 IS_AMOEBOID(Feld[right_x][right_y])));
6905 if (can_turn_left && can_turn_right)
6906 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6907 else if (can_turn_left)
6908 MovDir[x][y] = left_dir;
6910 MovDir[x][y] = right_dir;
6913 if (MovDir[x][y] != old_move_dir)
6916 else if (element == EL_BALLOON)
6918 MovDir[x][y] = game.wind_direction;
6921 else if (element == EL_SPRING)
6923 #if USE_NEW_SPRING_BUMPER
6924 if (MovDir[x][y] & MV_HORIZONTAL)
6926 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6927 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6929 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6930 ResetGfxAnimation(move_x, move_y);
6931 DrawLevelField(move_x, move_y);
6933 MovDir[x][y] = back_dir;
6935 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6936 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6937 MovDir[x][y] = MV_NONE;
6940 if (MovDir[x][y] & MV_HORIZONTAL &&
6941 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6942 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
6943 MovDir[x][y] = MV_NONE;
6948 else if (element == EL_ROBOT ||
6949 element == EL_SATELLITE ||
6950 element == EL_PENGUIN ||
6951 element == EL_EMC_ANDROID)
6953 int attr_x = -1, attr_y = -1;
6964 for (i = 0; i < MAX_PLAYERS; i++)
6966 struct PlayerInfo *player = &stored_player[i];
6967 int jx = player->jx, jy = player->jy;
6969 if (!player->active)
6973 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6981 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6982 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6983 game.engine_version < VERSION_IDENT(3,1,0,0)))
6989 if (element == EL_PENGUIN)
6992 static int xy[4][2] =
7000 for (i = 0; i < NUM_DIRECTIONS; i++)
7002 int ex = x + xy[i][0];
7003 int ey = y + xy[i][1];
7005 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7006 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7007 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7008 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7017 MovDir[x][y] = MV_NONE;
7019 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7020 else if (attr_x > x)
7021 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7023 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7024 else if (attr_y > y)
7025 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7027 if (element == EL_ROBOT)
7031 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7032 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7033 Moving2Blocked(x, y, &newx, &newy);
7035 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7036 MovDelay[x][y] = 8 + 8 * !RND(3);
7038 MovDelay[x][y] = 16;
7040 else if (element == EL_PENGUIN)
7046 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7048 boolean first_horiz = RND(2);
7049 int new_move_dir = MovDir[x][y];
7052 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7053 Moving2Blocked(x, y, &newx, &newy);
7055 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7059 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7060 Moving2Blocked(x, y, &newx, &newy);
7062 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7065 MovDir[x][y] = old_move_dir;
7069 else if (element == EL_SATELLITE)
7075 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7077 boolean first_horiz = RND(2);
7078 int new_move_dir = MovDir[x][y];
7081 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7082 Moving2Blocked(x, y, &newx, &newy);
7084 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7088 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7089 Moving2Blocked(x, y, &newx, &newy);
7091 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7094 MovDir[x][y] = old_move_dir;
7098 else if (element == EL_EMC_ANDROID)
7100 static int check_pos[16] =
7102 -1, /* 0 => (invalid) */
7103 7, /* 1 => MV_LEFT */
7104 3, /* 2 => MV_RIGHT */
7105 -1, /* 3 => (invalid) */
7107 0, /* 5 => MV_LEFT | MV_UP */
7108 2, /* 6 => MV_RIGHT | MV_UP */
7109 -1, /* 7 => (invalid) */
7110 5, /* 8 => MV_DOWN */
7111 6, /* 9 => MV_LEFT | MV_DOWN */
7112 4, /* 10 => MV_RIGHT | MV_DOWN */
7113 -1, /* 11 => (invalid) */
7114 -1, /* 12 => (invalid) */
7115 -1, /* 13 => (invalid) */
7116 -1, /* 14 => (invalid) */
7117 -1, /* 15 => (invalid) */
7125 { -1, -1, MV_LEFT | MV_UP },
7127 { +1, -1, MV_RIGHT | MV_UP },
7128 { +1, 0, MV_RIGHT },
7129 { +1, +1, MV_RIGHT | MV_DOWN },
7131 { -1, +1, MV_LEFT | MV_DOWN },
7134 int start_pos, check_order;
7135 boolean can_clone = FALSE;
7138 /* check if there is any free field around current position */
7139 for (i = 0; i < 8; i++)
7141 int newx = x + check_xy[i].dx;
7142 int newy = y + check_xy[i].dy;
7144 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7152 if (can_clone) /* randomly find an element to clone */
7156 start_pos = check_pos[RND(8)];
7157 check_order = (RND(2) ? -1 : +1);
7159 for (i = 0; i < 8; i++)
7161 int pos_raw = start_pos + i * check_order;
7162 int pos = (pos_raw + 8) % 8;
7163 int newx = x + check_xy[pos].dx;
7164 int newy = y + check_xy[pos].dy;
7166 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7168 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7169 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7171 Store[x][y] = Feld[newx][newy];
7180 if (can_clone) /* randomly find a direction to move */
7184 start_pos = check_pos[RND(8)];
7185 check_order = (RND(2) ? -1 : +1);
7187 for (i = 0; i < 8; i++)
7189 int pos_raw = start_pos + i * check_order;
7190 int pos = (pos_raw + 8) % 8;
7191 int newx = x + check_xy[pos].dx;
7192 int newy = y + check_xy[pos].dy;
7193 int new_move_dir = check_xy[pos].dir;
7195 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7197 MovDir[x][y] = new_move_dir;
7198 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7207 if (can_clone) /* cloning and moving successful */
7210 /* cannot clone -- try to move towards player */
7212 start_pos = check_pos[MovDir[x][y] & 0x0f];
7213 check_order = (RND(2) ? -1 : +1);
7215 for (i = 0; i < 3; i++)
7217 /* first check start_pos, then previous/next or (next/previous) pos */
7218 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7219 int pos = (pos_raw + 8) % 8;
7220 int newx = x + check_xy[pos].dx;
7221 int newy = y + check_xy[pos].dy;
7222 int new_move_dir = check_xy[pos].dir;
7224 if (IS_PLAYER(newx, newy))
7227 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7229 MovDir[x][y] = new_move_dir;
7230 MovDelay[x][y] = level.android_move_time * 8 + 1;
7237 else if (move_pattern == MV_TURNING_LEFT ||
7238 move_pattern == MV_TURNING_RIGHT ||
7239 move_pattern == MV_TURNING_LEFT_RIGHT ||
7240 move_pattern == MV_TURNING_RIGHT_LEFT ||
7241 move_pattern == MV_TURNING_RANDOM ||
7242 move_pattern == MV_ALL_DIRECTIONS)
7244 boolean can_turn_left =
7245 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7246 boolean can_turn_right =
7247 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7249 if (element_info[element].move_stepsize == 0) /* "not moving" */
7252 if (move_pattern == MV_TURNING_LEFT)
7253 MovDir[x][y] = left_dir;
7254 else if (move_pattern == MV_TURNING_RIGHT)
7255 MovDir[x][y] = right_dir;
7256 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7257 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7258 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7259 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7260 else if (move_pattern == MV_TURNING_RANDOM)
7261 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7262 can_turn_right && !can_turn_left ? right_dir :
7263 RND(2) ? left_dir : right_dir);
7264 else if (can_turn_left && can_turn_right)
7265 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7266 else if (can_turn_left)
7267 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7268 else if (can_turn_right)
7269 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7271 MovDir[x][y] = back_dir;
7273 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7275 else if (move_pattern == MV_HORIZONTAL ||
7276 move_pattern == MV_VERTICAL)
7278 if (move_pattern & old_move_dir)
7279 MovDir[x][y] = back_dir;
7280 else if (move_pattern == MV_HORIZONTAL)
7281 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7282 else if (move_pattern == MV_VERTICAL)
7283 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7285 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7287 else if (move_pattern & MV_ANY_DIRECTION)
7289 MovDir[x][y] = move_pattern;
7290 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7292 else if (move_pattern & MV_WIND_DIRECTION)
7294 MovDir[x][y] = game.wind_direction;
7295 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7297 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7299 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7300 MovDir[x][y] = left_dir;
7301 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7302 MovDir[x][y] = right_dir;
7304 if (MovDir[x][y] != old_move_dir)
7305 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7307 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7309 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7310 MovDir[x][y] = right_dir;
7311 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7312 MovDir[x][y] = left_dir;
7314 if (MovDir[x][y] != old_move_dir)
7315 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7317 else if (move_pattern == MV_TOWARDS_PLAYER ||
7318 move_pattern == MV_AWAY_FROM_PLAYER)
7320 int attr_x = -1, attr_y = -1;
7322 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7333 for (i = 0; i < MAX_PLAYERS; i++)
7335 struct PlayerInfo *player = &stored_player[i];
7336 int jx = player->jx, jy = player->jy;
7338 if (!player->active)
7342 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7350 MovDir[x][y] = MV_NONE;
7352 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7353 else if (attr_x > x)
7354 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7356 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7357 else if (attr_y > y)
7358 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7360 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7362 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7364 boolean first_horiz = RND(2);
7365 int new_move_dir = MovDir[x][y];
7367 if (element_info[element].move_stepsize == 0) /* "not moving" */
7369 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7370 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7376 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7377 Moving2Blocked(x, y, &newx, &newy);
7379 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7383 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7384 Moving2Blocked(x, y, &newx, &newy);
7386 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7389 MovDir[x][y] = old_move_dir;
7392 else if (move_pattern == MV_WHEN_PUSHED ||
7393 move_pattern == MV_WHEN_DROPPED)
7395 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7396 MovDir[x][y] = MV_NONE;
7400 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7402 static int test_xy[7][2] =
7412 static int test_dir[7] =
7422 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7423 int move_preference = -1000000; /* start with very low preference */
7424 int new_move_dir = MV_NONE;
7425 int start_test = RND(4);
7428 for (i = 0; i < NUM_DIRECTIONS; i++)
7430 int move_dir = test_dir[start_test + i];
7431 int move_dir_preference;
7433 xx = x + test_xy[start_test + i][0];
7434 yy = y + test_xy[start_test + i][1];
7436 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7437 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7439 new_move_dir = move_dir;
7444 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7447 move_dir_preference = -1 * RunnerVisit[xx][yy];
7448 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7449 move_dir_preference = PlayerVisit[xx][yy];
7451 if (move_dir_preference > move_preference)
7453 /* prefer field that has not been visited for the longest time */
7454 move_preference = move_dir_preference;
7455 new_move_dir = move_dir;
7457 else if (move_dir_preference == move_preference &&
7458 move_dir == old_move_dir)
7460 /* prefer last direction when all directions are preferred equally */
7461 move_preference = move_dir_preference;
7462 new_move_dir = move_dir;
7466 MovDir[x][y] = new_move_dir;
7467 if (old_move_dir != new_move_dir)
7468 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7472 static void TurnRound(int x, int y)
7474 int direction = MovDir[x][y];
7478 GfxDir[x][y] = MovDir[x][y];
7480 if (direction != MovDir[x][y])
7484 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7486 ResetGfxFrame(x, y, FALSE);
7489 static boolean JustBeingPushed(int x, int y)
7493 for (i = 0; i < MAX_PLAYERS; i++)
7495 struct PlayerInfo *player = &stored_player[i];
7497 if (player->active && player->is_pushing && player->MovPos)
7499 int next_jx = player->jx + (player->jx - player->last_jx);
7500 int next_jy = player->jy + (player->jy - player->last_jy);
7502 if (x == next_jx && y == next_jy)
7510 void StartMoving(int x, int y)
7512 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7513 int element = Feld[x][y];
7518 if (MovDelay[x][y] == 0)
7519 GfxAction[x][y] = ACTION_DEFAULT;
7521 if (CAN_FALL(element) && y < lev_fieldy - 1)
7523 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7524 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7525 if (JustBeingPushed(x, y))
7528 if (element == EL_QUICKSAND_FULL)
7530 if (IS_FREE(x, y + 1))
7532 InitMovingField(x, y, MV_DOWN);
7533 started_moving = TRUE;
7535 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7536 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7537 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7538 Store[x][y] = EL_ROCK;
7540 Store[x][y] = EL_ROCK;
7543 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7545 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7547 if (!MovDelay[x][y])
7548 MovDelay[x][y] = TILEY + 1;
7557 Feld[x][y] = EL_QUICKSAND_EMPTY;
7558 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7559 Store[x][y + 1] = Store[x][y];
7562 PlayLevelSoundAction(x, y, ACTION_FILLING);
7565 else if (element == EL_QUICKSAND_FAST_FULL)
7567 if (IS_FREE(x, y + 1))
7569 InitMovingField(x, y, MV_DOWN);
7570 started_moving = TRUE;
7572 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7573 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7574 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7575 Store[x][y] = EL_ROCK;
7577 Store[x][y] = EL_ROCK;
7580 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7582 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7584 if (!MovDelay[x][y])
7585 MovDelay[x][y] = TILEY + 1;
7594 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7595 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7596 Store[x][y + 1] = Store[x][y];
7599 PlayLevelSoundAction(x, y, ACTION_FILLING);
7602 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7603 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7605 InitMovingField(x, y, MV_DOWN);
7606 started_moving = TRUE;
7608 Feld[x][y] = EL_QUICKSAND_FILLING;
7609 Store[x][y] = element;
7611 PlayLevelSoundAction(x, y, ACTION_FILLING);
7613 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7614 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7616 InitMovingField(x, y, MV_DOWN);
7617 started_moving = TRUE;
7619 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7620 Store[x][y] = element;
7622 PlayLevelSoundAction(x, y, ACTION_FILLING);
7624 else if (element == EL_MAGIC_WALL_FULL)
7626 if (IS_FREE(x, y + 1))
7628 InitMovingField(x, y, MV_DOWN);
7629 started_moving = TRUE;
7631 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7632 Store[x][y] = EL_CHANGED(Store[x][y]);
7634 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7636 if (!MovDelay[x][y])
7637 MovDelay[x][y] = TILEY/4 + 1;
7646 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7647 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7648 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7652 else if (element == EL_BD_MAGIC_WALL_FULL)
7654 if (IS_FREE(x, y + 1))
7656 InitMovingField(x, y, MV_DOWN);
7657 started_moving = TRUE;
7659 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7660 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7662 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7664 if (!MovDelay[x][y])
7665 MovDelay[x][y] = TILEY/4 + 1;
7674 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7675 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7676 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7680 else if (element == EL_DC_MAGIC_WALL_FULL)
7682 if (IS_FREE(x, y + 1))
7684 InitMovingField(x, y, MV_DOWN);
7685 started_moving = TRUE;
7687 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7688 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7690 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7692 if (!MovDelay[x][y])
7693 MovDelay[x][y] = TILEY/4 + 1;
7702 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7703 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7704 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7708 else if ((CAN_PASS_MAGIC_WALL(element) &&
7709 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7710 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7711 (CAN_PASS_DC_MAGIC_WALL(element) &&
7712 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7715 InitMovingField(x, y, MV_DOWN);
7716 started_moving = TRUE;
7719 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7720 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7721 EL_DC_MAGIC_WALL_FILLING);
7722 Store[x][y] = element;
7724 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7726 SplashAcid(x, y + 1);
7728 InitMovingField(x, y, MV_DOWN);
7729 started_moving = TRUE;
7731 Store[x][y] = EL_ACID;
7734 #if USE_FIX_IMPACT_COLLISION
7735 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7736 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7738 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7739 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7741 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7742 CAN_FALL(element) && WasJustFalling[x][y] &&
7743 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7745 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7746 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7747 (Feld[x][y + 1] == EL_BLOCKED)))
7749 /* this is needed for a special case not covered by calling "Impact()"
7750 from "ContinueMoving()": if an element moves to a tile directly below
7751 another element which was just falling on that tile (which was empty
7752 in the previous frame), the falling element above would just stop
7753 instead of smashing the element below (in previous version, the above
7754 element was just checked for "moving" instead of "falling", resulting
7755 in incorrect smashes caused by horizontal movement of the above
7756 element; also, the case of the player being the element to smash was
7757 simply not covered here... :-/ ) */
7759 CheckCollision[x][y] = 0;
7760 CheckImpact[x][y] = 0;
7764 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7766 if (MovDir[x][y] == MV_NONE)
7768 InitMovingField(x, y, MV_DOWN);
7769 started_moving = TRUE;
7772 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7774 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7775 MovDir[x][y] = MV_DOWN;
7777 InitMovingField(x, y, MV_DOWN);
7778 started_moving = TRUE;
7780 else if (element == EL_AMOEBA_DROP)
7782 Feld[x][y] = EL_AMOEBA_GROWING;
7783 Store[x][y] = EL_AMOEBA_WET;
7785 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7786 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7787 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7788 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7790 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
7791 (IS_FREE(x - 1, y + 1) ||
7792 Feld[x - 1][y + 1] == EL_ACID));
7793 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7794 (IS_FREE(x + 1, y + 1) ||
7795 Feld[x + 1][y + 1] == EL_ACID));
7796 boolean can_fall_any = (can_fall_left || can_fall_right);
7797 boolean can_fall_both = (can_fall_left && can_fall_right);
7798 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7800 #if USE_NEW_ALL_SLIPPERY
7801 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7803 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7804 can_fall_right = FALSE;
7805 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7806 can_fall_left = FALSE;
7807 else if (slippery_type == SLIPPERY_ONLY_LEFT)
7808 can_fall_right = FALSE;
7809 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7810 can_fall_left = FALSE;
7812 can_fall_any = (can_fall_left || can_fall_right);
7813 can_fall_both = FALSE;
7816 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
7818 if (slippery_type == SLIPPERY_ONLY_LEFT)
7819 can_fall_right = FALSE;
7820 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7821 can_fall_left = FALSE;
7822 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7823 can_fall_right = FALSE;
7824 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7825 can_fall_left = FALSE;
7827 can_fall_any = (can_fall_left || can_fall_right);
7828 can_fall_both = (can_fall_left && can_fall_right);
7832 #if USE_NEW_ALL_SLIPPERY
7834 #if USE_NEW_SP_SLIPPERY
7835 /* !!! better use the same properties as for custom elements here !!! */
7836 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
7837 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
7839 can_fall_right = FALSE; /* slip down on left side */
7840 can_fall_both = FALSE;
7845 #if USE_NEW_ALL_SLIPPERY
7848 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7849 can_fall_right = FALSE; /* slip down on left side */
7851 can_fall_left = !(can_fall_right = RND(2));
7853 can_fall_both = FALSE;
7858 if (game.emulation == EMU_BOULDERDASH ||
7859 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7860 can_fall_right = FALSE; /* slip down on left side */
7862 can_fall_left = !(can_fall_right = RND(2));
7864 can_fall_both = FALSE;
7870 /* if not determined otherwise, prefer left side for slipping down */
7871 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7872 started_moving = TRUE;
7876 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
7878 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7881 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
7882 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7883 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7884 int belt_dir = game.belt_dir[belt_nr];
7886 if ((belt_dir == MV_LEFT && left_is_free) ||
7887 (belt_dir == MV_RIGHT && right_is_free))
7889 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7891 InitMovingField(x, y, belt_dir);
7892 started_moving = TRUE;
7894 Pushed[x][y] = TRUE;
7895 Pushed[nextx][y] = TRUE;
7897 GfxAction[x][y] = ACTION_DEFAULT;
7901 MovDir[x][y] = 0; /* if element was moving, stop it */
7906 /* not "else if" because of elements that can fall and move (EL_SPRING) */
7908 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
7910 if (CAN_MOVE(element) && !started_moving)
7913 int move_pattern = element_info[element].move_pattern;
7918 if (MovDir[x][y] == MV_NONE)
7920 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
7921 x, y, element, element_info[element].token_name);
7922 printf("StartMoving(): This should never happen!\n");
7927 Moving2Blocked(x, y, &newx, &newy);
7929 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7932 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7933 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7935 WasJustMoving[x][y] = 0;
7936 CheckCollision[x][y] = 0;
7938 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7940 if (Feld[x][y] != element) /* element has changed */
7944 if (!MovDelay[x][y]) /* start new movement phase */
7946 /* all objects that can change their move direction after each step
7947 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7949 if (element != EL_YAMYAM &&
7950 element != EL_DARK_YAMYAM &&
7951 element != EL_PACMAN &&
7952 !(move_pattern & MV_ANY_DIRECTION) &&
7953 move_pattern != MV_TURNING_LEFT &&
7954 move_pattern != MV_TURNING_RIGHT &&
7955 move_pattern != MV_TURNING_LEFT_RIGHT &&
7956 move_pattern != MV_TURNING_RIGHT_LEFT &&
7957 move_pattern != MV_TURNING_RANDOM)
7961 if (MovDelay[x][y] && (element == EL_BUG ||
7962 element == EL_SPACESHIP ||
7963 element == EL_SP_SNIKSNAK ||
7964 element == EL_SP_ELECTRON ||
7965 element == EL_MOLE))
7966 DrawLevelField(x, y);
7970 if (MovDelay[x][y]) /* wait some time before next movement */
7974 if (element == EL_ROBOT ||
7975 element == EL_YAMYAM ||
7976 element == EL_DARK_YAMYAM)
7978 DrawLevelElementAnimationIfNeeded(x, y, element);
7979 PlayLevelSoundAction(x, y, ACTION_WAITING);
7981 else if (element == EL_SP_ELECTRON)
7982 DrawLevelElementAnimationIfNeeded(x, y, element);
7983 else if (element == EL_DRAGON)
7986 int dir = MovDir[x][y];
7987 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7988 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
7989 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
7990 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
7991 dir == MV_UP ? IMG_FLAMES_1_UP :
7992 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7993 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7995 GfxAction[x][y] = ACTION_ATTACKING;
7997 if (IS_PLAYER(x, y))
7998 DrawPlayerField(x, y);
8000 DrawLevelField(x, y);
8002 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8004 for (i = 1; i <= 3; i++)
8006 int xx = x + i * dx;
8007 int yy = y + i * dy;
8008 int sx = SCREENX(xx);
8009 int sy = SCREENY(yy);
8010 int flame_graphic = graphic + (i - 1);
8012 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8017 int flamed = MovingOrBlocked2Element(xx, yy);
8021 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8023 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8024 RemoveMovingField(xx, yy);
8026 RemoveField(xx, yy);
8028 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8031 RemoveMovingField(xx, yy);
8034 ChangeDelay[xx][yy] = 0;
8036 Feld[xx][yy] = EL_FLAMES;
8038 if (IN_SCR_FIELD(sx, sy))
8040 DrawLevelFieldCrumbledSand(xx, yy);
8041 DrawGraphic(sx, sy, flame_graphic, frame);
8046 if (Feld[xx][yy] == EL_FLAMES)
8047 Feld[xx][yy] = EL_EMPTY;
8048 DrawLevelField(xx, yy);
8053 if (MovDelay[x][y]) /* element still has to wait some time */
8055 PlayLevelSoundAction(x, y, ACTION_WAITING);
8061 /* now make next step */
8063 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8065 if (DONT_COLLIDE_WITH(element) &&
8066 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8067 !PLAYER_ENEMY_PROTECTED(newx, newy))
8069 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8074 else if (CAN_MOVE_INTO_ACID(element) &&
8075 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8076 !IS_MV_DIAGONAL(MovDir[x][y]) &&
8077 (MovDir[x][y] == MV_DOWN ||
8078 game.engine_version >= VERSION_IDENT(3,1,0,0)))
8080 SplashAcid(newx, newy);
8081 Store[x][y] = EL_ACID;
8083 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8085 if (Feld[newx][newy] == EL_EXIT_OPEN ||
8086 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8087 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8088 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8091 DrawLevelField(x, y);
8093 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8094 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8095 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8097 local_player->friends_still_needed--;
8098 if (!local_player->friends_still_needed &&
8099 !local_player->GameOver && AllPlayersGone)
8100 PlayerWins(local_player);
8104 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8106 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8107 DrawLevelField(newx, newy);
8109 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8111 else if (!IS_FREE(newx, newy))
8113 GfxAction[x][y] = ACTION_WAITING;
8115 if (IS_PLAYER(x, y))
8116 DrawPlayerField(x, y);
8118 DrawLevelField(x, y);
8123 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8125 if (IS_FOOD_PIG(Feld[newx][newy]))
8127 if (IS_MOVING(newx, newy))
8128 RemoveMovingField(newx, newy);
8131 Feld[newx][newy] = EL_EMPTY;
8132 DrawLevelField(newx, newy);
8135 PlayLevelSound(x, y, SND_PIG_DIGGING);
8137 else if (!IS_FREE(newx, newy))
8139 if (IS_PLAYER(x, y))
8140 DrawPlayerField(x, y);
8142 DrawLevelField(x, y);
8147 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8149 if (Store[x][y] != EL_EMPTY)
8151 boolean can_clone = FALSE;
8154 /* check if element to clone is still there */
8155 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8157 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8165 /* cannot clone or target field not free anymore -- do not clone */
8166 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8167 Store[x][y] = EL_EMPTY;
8170 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8172 if (IS_MV_DIAGONAL(MovDir[x][y]))
8174 int diagonal_move_dir = MovDir[x][y];
8175 int stored = Store[x][y];
8176 int change_delay = 8;
8179 /* android is moving diagonally */
8181 CreateField(x, y, EL_DIAGONAL_SHRINKING);
8183 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8184 GfxElement[x][y] = EL_EMC_ANDROID;
8185 GfxAction[x][y] = ACTION_SHRINKING;
8186 GfxDir[x][y] = diagonal_move_dir;
8187 ChangeDelay[x][y] = change_delay;
8189 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8192 DrawLevelGraphicAnimation(x, y, graphic);
8193 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8195 if (Feld[newx][newy] == EL_ACID)
8197 SplashAcid(newx, newy);
8202 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8204 Store[newx][newy] = EL_EMC_ANDROID;
8205 GfxElement[newx][newy] = EL_EMC_ANDROID;
8206 GfxAction[newx][newy] = ACTION_GROWING;
8207 GfxDir[newx][newy] = diagonal_move_dir;
8208 ChangeDelay[newx][newy] = change_delay;
8210 graphic = el_act_dir2img(GfxElement[newx][newy],
8211 GfxAction[newx][newy], GfxDir[newx][newy]);
8213 DrawLevelGraphicAnimation(newx, newy, graphic);
8214 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8220 Feld[newx][newy] = EL_EMPTY;
8221 DrawLevelField(newx, newy);
8223 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8226 else if (!IS_FREE(newx, newy))
8229 if (IS_PLAYER(x, y))
8230 DrawPlayerField(x, y);
8232 DrawLevelField(x, y);
8238 else if (IS_CUSTOM_ELEMENT(element) &&
8239 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8241 int new_element = Feld[newx][newy];
8243 if (!IS_FREE(newx, newy))
8245 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8246 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8249 /* no element can dig solid indestructible elements */
8250 if (IS_INDESTRUCTIBLE(new_element) &&
8251 !IS_DIGGABLE(new_element) &&
8252 !IS_COLLECTIBLE(new_element))
8255 if (AmoebaNr[newx][newy] &&
8256 (new_element == EL_AMOEBA_FULL ||
8257 new_element == EL_BD_AMOEBA ||
8258 new_element == EL_AMOEBA_GROWING))
8260 AmoebaCnt[AmoebaNr[newx][newy]]--;
8261 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8264 if (IS_MOVING(newx, newy))
8265 RemoveMovingField(newx, newy);
8268 RemoveField(newx, newy);
8269 DrawLevelField(newx, newy);
8272 /* if digged element was about to explode, prevent the explosion */
8273 ExplodeField[newx][newy] = EX_TYPE_NONE;
8275 PlayLevelSoundAction(x, y, action);
8278 Store[newx][newy] = EL_EMPTY;
8280 /* this makes it possible to leave the removed element again */
8281 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8282 Store[newx][newy] = new_element;
8284 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8286 int move_leave_element = element_info[element].move_leave_element;
8288 /* this makes it possible to leave the removed element again */
8289 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8290 new_element : move_leave_element);
8294 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8296 RunnerVisit[x][y] = FrameCounter;
8297 PlayerVisit[x][y] /= 8; /* expire player visit path */
8300 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8302 if (!IS_FREE(newx, newy))
8304 if (IS_PLAYER(x, y))
8305 DrawPlayerField(x, y);
8307 DrawLevelField(x, y);
8313 boolean wanna_flame = !RND(10);
8314 int dx = newx - x, dy = newy - y;
8315 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8316 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8317 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8318 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8319 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8320 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8323 IS_CLASSIC_ENEMY(element1) ||
8324 IS_CLASSIC_ENEMY(element2)) &&
8325 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8326 element1 != EL_FLAMES && element2 != EL_FLAMES)
8328 ResetGfxAnimation(x, y);
8329 GfxAction[x][y] = ACTION_ATTACKING;
8331 if (IS_PLAYER(x, y))
8332 DrawPlayerField(x, y);
8334 DrawLevelField(x, y);
8336 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8338 MovDelay[x][y] = 50;
8342 RemoveField(newx, newy);
8344 Feld[newx][newy] = EL_FLAMES;
8345 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8348 RemoveField(newx1, newy1);
8350 Feld[newx1][newy1] = EL_FLAMES;
8352 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8355 RemoveField(newx2, newy2);
8357 Feld[newx2][newy2] = EL_FLAMES;
8364 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8365 Feld[newx][newy] == EL_DIAMOND)
8367 if (IS_MOVING(newx, newy))
8368 RemoveMovingField(newx, newy);
8371 Feld[newx][newy] = EL_EMPTY;
8372 DrawLevelField(newx, newy);
8375 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8377 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8378 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8380 if (AmoebaNr[newx][newy])
8382 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8383 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8384 Feld[newx][newy] == EL_BD_AMOEBA)
8385 AmoebaCnt[AmoebaNr[newx][newy]]--;
8390 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8392 RemoveMovingField(newx, newy);
8395 if (IS_MOVING(newx, newy))
8397 RemoveMovingField(newx, newy);
8402 Feld[newx][newy] = EL_EMPTY;
8403 DrawLevelField(newx, newy);
8406 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8408 else if ((element == EL_PACMAN || element == EL_MOLE)
8409 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8411 if (AmoebaNr[newx][newy])
8413 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8414 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8415 Feld[newx][newy] == EL_BD_AMOEBA)
8416 AmoebaCnt[AmoebaNr[newx][newy]]--;
8419 if (element == EL_MOLE)
8421 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8422 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8424 ResetGfxAnimation(x, y);
8425 GfxAction[x][y] = ACTION_DIGGING;
8426 DrawLevelField(x, y);
8428 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8430 return; /* wait for shrinking amoeba */
8432 else /* element == EL_PACMAN */
8434 Feld[newx][newy] = EL_EMPTY;
8435 DrawLevelField(newx, newy);
8436 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8439 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8440 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8441 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8443 /* wait for shrinking amoeba to completely disappear */
8446 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8448 /* object was running against a wall */
8453 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8454 if (move_pattern & MV_ANY_DIRECTION &&
8455 move_pattern == MovDir[x][y])
8457 int blocking_element =
8458 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8460 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8463 element = Feld[x][y]; /* element might have changed */
8467 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8468 DrawLevelElementAnimation(x, y, element);
8470 if (DONT_TOUCH(element))
8471 TestIfBadThingTouchesPlayer(x, y);
8476 InitMovingField(x, y, MovDir[x][y]);
8478 PlayLevelSoundAction(x, y, ACTION_MOVING);
8482 ContinueMoving(x, y);
8485 void ContinueMoving(int x, int y)
8487 int element = Feld[x][y];
8488 struct ElementInfo *ei = &element_info[element];
8489 int direction = MovDir[x][y];
8490 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8491 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8492 int newx = x + dx, newy = y + dy;
8493 int stored = Store[x][y];
8494 int stored_new = Store[newx][newy];
8495 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
8496 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8497 boolean last_line = (newy == lev_fieldy - 1);
8499 MovPos[x][y] += getElementMoveStepsize(x, y);
8501 if (pushed_by_player) /* special case: moving object pushed by player */
8502 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8504 if (ABS(MovPos[x][y]) < TILEX)
8507 int ee = Feld[x][y];
8508 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8509 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8511 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8512 x, y, ABS(MovPos[x][y]),
8514 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8517 DrawLevelField(x, y);
8519 return; /* element is still moving */
8522 /* element reached destination field */
8524 Feld[x][y] = EL_EMPTY;
8525 Feld[newx][newy] = element;
8526 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8528 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
8530 element = Feld[newx][newy] = EL_ACID;
8532 else if (element == EL_MOLE)
8534 Feld[x][y] = EL_SAND;
8536 DrawLevelFieldCrumbledSandNeighbours(x, y);
8538 else if (element == EL_QUICKSAND_FILLING)
8540 element = Feld[newx][newy] = get_next_element(element);
8541 Store[newx][newy] = Store[x][y];
8543 else if (element == EL_QUICKSAND_EMPTYING)
8545 Feld[x][y] = get_next_element(element);
8546 element = Feld[newx][newy] = Store[x][y];
8548 else if (element == EL_QUICKSAND_FAST_FILLING)
8550 element = Feld[newx][newy] = get_next_element(element);
8551 Store[newx][newy] = Store[x][y];
8553 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8555 Feld[x][y] = get_next_element(element);
8556 element = Feld[newx][newy] = Store[x][y];
8558 else if (element == EL_MAGIC_WALL_FILLING)
8560 element = Feld[newx][newy] = get_next_element(element);
8561 if (!game.magic_wall_active)
8562 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8563 Store[newx][newy] = Store[x][y];
8565 else if (element == EL_MAGIC_WALL_EMPTYING)
8567 Feld[x][y] = get_next_element(element);
8568 if (!game.magic_wall_active)
8569 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8570 element = Feld[newx][newy] = Store[x][y];
8572 #if USE_NEW_CUSTOM_VALUE
8573 InitField(newx, newy, FALSE);
8576 else if (element == EL_BD_MAGIC_WALL_FILLING)
8578 element = Feld[newx][newy] = get_next_element(element);
8579 if (!game.magic_wall_active)
8580 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8581 Store[newx][newy] = Store[x][y];
8583 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8585 Feld[x][y] = get_next_element(element);
8586 if (!game.magic_wall_active)
8587 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8588 element = Feld[newx][newy] = Store[x][y];
8590 #if USE_NEW_CUSTOM_VALUE
8591 InitField(newx, newy, FALSE);
8594 else if (element == EL_DC_MAGIC_WALL_FILLING)
8596 element = Feld[newx][newy] = get_next_element(element);
8597 if (!game.magic_wall_active)
8598 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8599 Store[newx][newy] = Store[x][y];
8601 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8603 Feld[x][y] = get_next_element(element);
8604 if (!game.magic_wall_active)
8605 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8606 element = Feld[newx][newy] = Store[x][y];
8608 #if USE_NEW_CUSTOM_VALUE
8609 InitField(newx, newy, FALSE);
8612 else if (element == EL_AMOEBA_DROPPING)
8614 Feld[x][y] = get_next_element(element);
8615 element = Feld[newx][newy] = Store[x][y];
8617 else if (element == EL_SOKOBAN_OBJECT)
8620 Feld[x][y] = Back[x][y];
8622 if (Back[newx][newy])
8623 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8625 Back[x][y] = Back[newx][newy] = 0;
8628 Store[x][y] = EL_EMPTY;
8633 MovDelay[newx][newy] = 0;
8635 if (CAN_CHANGE_OR_HAS_ACTION(element))
8637 /* copy element change control values to new field */
8638 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8639 ChangePage[newx][newy] = ChangePage[x][y];
8640 ChangeCount[newx][newy] = ChangeCount[x][y];
8641 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8644 #if USE_NEW_CUSTOM_VALUE
8645 CustomValue[newx][newy] = CustomValue[x][y];
8648 ChangeDelay[x][y] = 0;
8649 ChangePage[x][y] = -1;
8650 ChangeCount[x][y] = 0;
8651 ChangeEvent[x][y] = -1;
8653 #if USE_NEW_CUSTOM_VALUE
8654 CustomValue[x][y] = 0;
8657 /* copy animation control values to new field */
8658 GfxFrame[newx][newy] = GfxFrame[x][y];
8659 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
8660 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
8661 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
8663 Pushed[x][y] = Pushed[newx][newy] = FALSE;
8665 /* some elements can leave other elements behind after moving */
8667 if (ei->move_leave_element != EL_EMPTY &&
8668 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8669 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8671 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
8672 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8673 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8676 int move_leave_element = ei->move_leave_element;
8680 /* this makes it possible to leave the removed element again */
8681 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8682 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8684 /* this makes it possible to leave the removed element again */
8685 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8686 move_leave_element = stored;
8689 /* this makes it possible to leave the removed element again */
8690 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8691 ei->move_leave_element == EL_TRIGGER_ELEMENT)
8692 move_leave_element = stored;
8695 Feld[x][y] = move_leave_element;
8697 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8698 MovDir[x][y] = direction;
8700 InitField(x, y, FALSE);
8702 if (GFX_CRUMBLED(Feld[x][y]))
8703 DrawLevelFieldCrumbledSandNeighbours(x, y);
8705 if (ELEM_IS_PLAYER(move_leave_element))
8706 RelocatePlayer(x, y, move_leave_element);
8709 /* do this after checking for left-behind element */
8710 ResetGfxAnimation(x, y); /* reset animation values for old field */
8712 if (!CAN_MOVE(element) ||
8713 (CAN_FALL(element) && direction == MV_DOWN &&
8714 (element == EL_SPRING ||
8715 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8716 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8717 GfxDir[x][y] = MovDir[newx][newy] = 0;
8719 DrawLevelField(x, y);
8720 DrawLevelField(newx, newy);
8722 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8724 /* prevent pushed element from moving on in pushed direction */
8725 if (pushed_by_player && CAN_MOVE(element) &&
8726 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8727 !(element_info[element].move_pattern & direction))
8728 TurnRound(newx, newy);
8730 /* prevent elements on conveyor belt from moving on in last direction */
8731 if (pushed_by_conveyor && CAN_FALL(element) &&
8732 direction & MV_HORIZONTAL)
8733 MovDir[newx][newy] = 0;
8735 if (!pushed_by_player)
8737 int nextx = newx + dx, nexty = newy + dy;
8738 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8740 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8742 if (CAN_FALL(element) && direction == MV_DOWN)
8743 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8745 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8746 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8748 #if USE_FIX_IMPACT_COLLISION
8749 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8750 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8754 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8756 TestIfBadThingTouchesPlayer(newx, newy);
8757 TestIfBadThingTouchesFriend(newx, newy);
8759 if (!IS_CUSTOM_ELEMENT(element))
8760 TestIfBadThingTouchesOtherBadThing(newx, newy);
8762 else if (element == EL_PENGUIN)
8763 TestIfFriendTouchesBadThing(newx, newy);
8765 /* give the player one last chance (one more frame) to move away */
8766 if (CAN_FALL(element) && direction == MV_DOWN &&
8767 (last_line || (!IS_FREE(x, newy + 1) &&
8768 (!IS_PLAYER(x, newy + 1) ||
8769 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8772 if (pushed_by_player && !game.use_change_when_pushing_bug)
8774 int push_side = MV_DIR_OPPOSITE(direction);
8775 struct PlayerInfo *player = PLAYERINFO(x, y);
8777 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8778 player->index_bit, push_side);
8779 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8780 player->index_bit, push_side);
8783 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
8784 MovDelay[newx][newy] = 1;
8786 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8788 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
8791 if (ChangePage[newx][newy] != -1) /* delayed change */
8793 int page = ChangePage[newx][newy];
8794 struct ElementChangeInfo *change = &ei->change_page[page];
8796 ChangePage[newx][newy] = -1;
8798 if (change->can_change)
8800 if (ChangeElement(newx, newy, element, page))
8802 if (change->post_change_function)
8803 change->post_change_function(newx, newy);
8807 if (change->has_action)
8808 ExecuteCustomElementAction(newx, newy, element, page);
8812 TestIfElementHitsCustomElement(newx, newy, direction);
8813 TestIfPlayerTouchesCustomElement(newx, newy);
8814 TestIfElementTouchesCustomElement(newx, newy);
8816 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8817 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8818 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8819 MV_DIR_OPPOSITE(direction));
8822 int AmoebeNachbarNr(int ax, int ay)
8825 int element = Feld[ax][ay];
8827 static int xy[4][2] =
8835 for (i = 0; i < NUM_DIRECTIONS; i++)
8837 int x = ax + xy[i][0];
8838 int y = ay + xy[i][1];
8840 if (!IN_LEV_FIELD(x, y))
8843 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8844 group_nr = AmoebaNr[x][y];
8850 void AmoebenVereinigen(int ax, int ay)
8852 int i, x, y, xx, yy;
8853 int new_group_nr = AmoebaNr[ax][ay];
8854 static int xy[4][2] =
8862 if (new_group_nr == 0)
8865 for (i = 0; i < NUM_DIRECTIONS; i++)
8870 if (!IN_LEV_FIELD(x, y))
8873 if ((Feld[x][y] == EL_AMOEBA_FULL ||
8874 Feld[x][y] == EL_BD_AMOEBA ||
8875 Feld[x][y] == EL_AMOEBA_DEAD) &&
8876 AmoebaNr[x][y] != new_group_nr)
8878 int old_group_nr = AmoebaNr[x][y];
8880 if (old_group_nr == 0)
8883 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8884 AmoebaCnt[old_group_nr] = 0;
8885 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8886 AmoebaCnt2[old_group_nr] = 0;
8888 SCAN_PLAYFIELD(xx, yy)
8890 if (AmoebaNr[xx][yy] == old_group_nr)
8891 AmoebaNr[xx][yy] = new_group_nr;
8897 void AmoebeUmwandeln(int ax, int ay)
8901 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8903 int group_nr = AmoebaNr[ax][ay];
8908 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8909 printf("AmoebeUmwandeln(): This should never happen!\n");
8914 SCAN_PLAYFIELD(x, y)
8916 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8919 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8923 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8924 SND_AMOEBA_TURNING_TO_GEM :
8925 SND_AMOEBA_TURNING_TO_ROCK));
8930 static int xy[4][2] =
8938 for (i = 0; i < NUM_DIRECTIONS; i++)
8943 if (!IN_LEV_FIELD(x, y))
8946 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8948 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8949 SND_AMOEBA_TURNING_TO_GEM :
8950 SND_AMOEBA_TURNING_TO_ROCK));
8957 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8960 int group_nr = AmoebaNr[ax][ay];
8961 boolean done = FALSE;
8966 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8967 printf("AmoebeUmwandelnBD(): This should never happen!\n");
8972 SCAN_PLAYFIELD(x, y)
8974 if (AmoebaNr[x][y] == group_nr &&
8975 (Feld[x][y] == EL_AMOEBA_DEAD ||
8976 Feld[x][y] == EL_BD_AMOEBA ||
8977 Feld[x][y] == EL_AMOEBA_GROWING))
8980 Feld[x][y] = new_element;
8981 InitField(x, y, FALSE);
8982 DrawLevelField(x, y);
8988 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8989 SND_BD_AMOEBA_TURNING_TO_ROCK :
8990 SND_BD_AMOEBA_TURNING_TO_GEM));
8993 void AmoebeWaechst(int x, int y)
8995 static unsigned long sound_delay = 0;
8996 static unsigned long sound_delay_value = 0;
8998 if (!MovDelay[x][y]) /* start new growing cycle */
9002 if (DelayReached(&sound_delay, sound_delay_value))
9004 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9005 sound_delay_value = 30;
9009 if (MovDelay[x][y]) /* wait some time before growing bigger */
9012 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9014 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9015 6 - MovDelay[x][y]);
9017 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9020 if (!MovDelay[x][y])
9022 Feld[x][y] = Store[x][y];
9024 DrawLevelField(x, y);
9029 void AmoebaDisappearing(int x, int y)
9031 static unsigned long sound_delay = 0;
9032 static unsigned long sound_delay_value = 0;
9034 if (!MovDelay[x][y]) /* start new shrinking cycle */
9038 if (DelayReached(&sound_delay, sound_delay_value))
9039 sound_delay_value = 30;
9042 if (MovDelay[x][y]) /* wait some time before shrinking */
9045 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9047 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9048 6 - MovDelay[x][y]);
9050 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9053 if (!MovDelay[x][y])
9055 Feld[x][y] = EL_EMPTY;
9056 DrawLevelField(x, y);
9058 /* don't let mole enter this field in this cycle;
9059 (give priority to objects falling to this field from above) */
9065 void AmoebeAbleger(int ax, int ay)
9068 int element = Feld[ax][ay];
9069 int graphic = el2img(element);
9070 int newax = ax, neway = ay;
9071 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9072 static int xy[4][2] =
9080 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9082 Feld[ax][ay] = EL_AMOEBA_DEAD;
9083 DrawLevelField(ax, ay);
9087 if (IS_ANIMATED(graphic))
9088 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9090 if (!MovDelay[ax][ay]) /* start making new amoeba field */
9091 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9093 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
9096 if (MovDelay[ax][ay])
9100 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9103 int x = ax + xy[start][0];
9104 int y = ay + xy[start][1];
9106 if (!IN_LEV_FIELD(x, y))
9109 if (IS_FREE(x, y) ||
9110 CAN_GROW_INTO(Feld[x][y]) ||
9111 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9112 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9118 if (newax == ax && neway == ay)
9121 else /* normal or "filled" (BD style) amoeba */
9124 boolean waiting_for_player = FALSE;
9126 for (i = 0; i < NUM_DIRECTIONS; i++)
9128 int j = (start + i) % 4;
9129 int x = ax + xy[j][0];
9130 int y = ay + xy[j][1];
9132 if (!IN_LEV_FIELD(x, y))
9135 if (IS_FREE(x, y) ||
9136 CAN_GROW_INTO(Feld[x][y]) ||
9137 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9138 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9144 else if (IS_PLAYER(x, y))
9145 waiting_for_player = TRUE;
9148 if (newax == ax && neway == ay) /* amoeba cannot grow */
9150 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9152 Feld[ax][ay] = EL_AMOEBA_DEAD;
9153 DrawLevelField(ax, ay);
9154 AmoebaCnt[AmoebaNr[ax][ay]]--;
9156 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
9158 if (element == EL_AMOEBA_FULL)
9159 AmoebeUmwandeln(ax, ay);
9160 else if (element == EL_BD_AMOEBA)
9161 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9166 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9168 /* amoeba gets larger by growing in some direction */
9170 int new_group_nr = AmoebaNr[ax][ay];
9173 if (new_group_nr == 0)
9175 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9176 printf("AmoebeAbleger(): This should never happen!\n");
9181 AmoebaNr[newax][neway] = new_group_nr;
9182 AmoebaCnt[new_group_nr]++;
9183 AmoebaCnt2[new_group_nr]++;
9185 /* if amoeba touches other amoeba(s) after growing, unify them */
9186 AmoebenVereinigen(newax, neway);
9188 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9190 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9196 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9197 (neway == lev_fieldy - 1 && newax != ax))
9199 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
9200 Store[newax][neway] = element;
9202 else if (neway == ay || element == EL_EMC_DRIPPER)
9204 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
9206 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9210 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
9211 Feld[ax][ay] = EL_AMOEBA_DROPPING;
9212 Store[ax][ay] = EL_AMOEBA_DROP;
9213 ContinueMoving(ax, ay);
9217 DrawLevelField(newax, neway);
9220 void Life(int ax, int ay)
9224 int element = Feld[ax][ay];
9225 int graphic = el2img(element);
9226 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9228 boolean changed = FALSE;
9230 if (IS_ANIMATED(graphic))
9231 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9236 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
9237 MovDelay[ax][ay] = life_time;
9239 if (MovDelay[ax][ay]) /* wait some time before next cycle */
9242 if (MovDelay[ax][ay])
9246 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9248 int xx = ax+x1, yy = ay+y1;
9251 if (!IN_LEV_FIELD(xx, yy))
9254 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9256 int x = xx+x2, y = yy+y2;
9258 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9261 if (((Feld[x][y] == element ||
9262 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9264 (IS_FREE(x, y) && Stop[x][y]))
9268 if (xx == ax && yy == ay) /* field in the middle */
9270 if (nachbarn < life_parameter[0] ||
9271 nachbarn > life_parameter[1])
9273 Feld[xx][yy] = EL_EMPTY;
9275 DrawLevelField(xx, yy);
9276 Stop[xx][yy] = TRUE;
9280 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9281 { /* free border field */
9282 if (nachbarn >= life_parameter[2] &&
9283 nachbarn <= life_parameter[3])
9285 Feld[xx][yy] = element;
9286 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9288 DrawLevelField(xx, yy);
9289 Stop[xx][yy] = TRUE;
9296 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9297 SND_GAME_OF_LIFE_GROWING);
9300 static void InitRobotWheel(int x, int y)
9302 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9305 static void RunRobotWheel(int x, int y)
9307 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9310 static void StopRobotWheel(int x, int y)
9312 if (ZX == x && ZY == y)
9316 game.robot_wheel_active = FALSE;
9320 static void InitTimegateWheel(int x, int y)
9322 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9325 static void RunTimegateWheel(int x, int y)
9327 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9330 static void InitMagicBallDelay(int x, int y)
9333 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9335 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9339 static void ActivateMagicBall(int bx, int by)
9343 if (level.ball_random)
9345 int pos_border = RND(8); /* select one of the eight border elements */
9346 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9347 int xx = pos_content % 3;
9348 int yy = pos_content / 3;
9353 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9354 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9358 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9360 int xx = x - bx + 1;
9361 int yy = y - by + 1;
9363 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9364 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9368 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9371 void CheckExit(int x, int y)
9373 if (local_player->gems_still_needed > 0 ||
9374 local_player->sokobanfields_still_needed > 0 ||
9375 local_player->lights_still_needed > 0)
9377 int element = Feld[x][y];
9378 int graphic = el2img(element);
9380 if (IS_ANIMATED(graphic))
9381 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9386 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9389 Feld[x][y] = EL_EXIT_OPENING;
9391 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9394 void CheckExitEM(int x, int y)
9396 if (local_player->gems_still_needed > 0 ||
9397 local_player->sokobanfields_still_needed > 0 ||
9398 local_player->lights_still_needed > 0)
9400 int element = Feld[x][y];
9401 int graphic = el2img(element);
9403 if (IS_ANIMATED(graphic))
9404 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9409 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9412 Feld[x][y] = EL_EM_EXIT_OPENING;
9414 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9417 void CheckExitSteel(int x, int y)
9419 if (local_player->gems_still_needed > 0 ||
9420 local_player->sokobanfields_still_needed > 0 ||
9421 local_player->lights_still_needed > 0)
9423 int element = Feld[x][y];
9424 int graphic = el2img(element);
9426 if (IS_ANIMATED(graphic))
9427 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9432 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9435 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9437 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9440 void CheckExitSteelEM(int x, int y)
9442 if (local_player->gems_still_needed > 0 ||
9443 local_player->sokobanfields_still_needed > 0 ||
9444 local_player->lights_still_needed > 0)
9446 int element = Feld[x][y];
9447 int graphic = el2img(element);
9449 if (IS_ANIMATED(graphic))
9450 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9455 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9458 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9460 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9463 void CheckExitSP(int x, int y)
9465 if (local_player->gems_still_needed > 0)
9467 int element = Feld[x][y];
9468 int graphic = el2img(element);
9470 if (IS_ANIMATED(graphic))
9471 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9476 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9479 Feld[x][y] = EL_SP_EXIT_OPENING;
9481 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9484 static void CloseAllOpenTimegates()
9488 SCAN_PLAYFIELD(x, y)
9490 int element = Feld[x][y];
9492 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9494 Feld[x][y] = EL_TIMEGATE_CLOSING;
9496 PlayLevelSoundAction(x, y, ACTION_CLOSING);
9501 void DrawTwinkleOnField(int x, int y)
9503 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9506 if (Feld[x][y] == EL_BD_DIAMOND)
9509 if (MovDelay[x][y] == 0) /* next animation frame */
9510 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9512 if (MovDelay[x][y] != 0) /* wait some time before next frame */
9516 DrawLevelElementAnimation(x, y, Feld[x][y]);
9518 if (MovDelay[x][y] != 0)
9520 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9521 10 - MovDelay[x][y]);
9523 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9528 void MauerWaechst(int x, int y)
9532 if (!MovDelay[x][y]) /* next animation frame */
9533 MovDelay[x][y] = 3 * delay;
9535 if (MovDelay[x][y]) /* wait some time before next frame */
9539 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9541 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9542 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9544 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9547 if (!MovDelay[x][y])
9549 if (MovDir[x][y] == MV_LEFT)
9551 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9552 DrawLevelField(x - 1, y);
9554 else if (MovDir[x][y] == MV_RIGHT)
9556 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9557 DrawLevelField(x + 1, y);
9559 else if (MovDir[x][y] == MV_UP)
9561 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9562 DrawLevelField(x, y - 1);
9566 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9567 DrawLevelField(x, y + 1);
9570 Feld[x][y] = Store[x][y];
9572 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9573 DrawLevelField(x, y);
9578 void MauerAbleger(int ax, int ay)
9580 int element = Feld[ax][ay];
9581 int graphic = el2img(element);
9582 boolean oben_frei = FALSE, unten_frei = FALSE;
9583 boolean links_frei = FALSE, rechts_frei = FALSE;
9584 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9585 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9586 boolean new_wall = FALSE;
9588 if (IS_ANIMATED(graphic))
9589 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9591 if (!MovDelay[ax][ay]) /* start building new wall */
9592 MovDelay[ax][ay] = 6;
9594 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9597 if (MovDelay[ax][ay])
9601 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9603 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9605 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9607 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9610 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9611 element == EL_EXPANDABLE_WALL_ANY)
9615 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9616 Store[ax][ay-1] = element;
9617 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9618 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9619 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9620 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9625 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9626 Store[ax][ay+1] = element;
9627 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9628 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9629 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9630 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9635 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9636 element == EL_EXPANDABLE_WALL_ANY ||
9637 element == EL_EXPANDABLE_WALL ||
9638 element == EL_BD_EXPANDABLE_WALL)
9642 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9643 Store[ax-1][ay] = element;
9644 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9645 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9646 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9647 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9653 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9654 Store[ax+1][ay] = element;
9655 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9656 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9657 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9658 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9663 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9664 DrawLevelField(ax, ay);
9666 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9668 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9669 unten_massiv = TRUE;
9670 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9671 links_massiv = TRUE;
9672 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9673 rechts_massiv = TRUE;
9675 if (((oben_massiv && unten_massiv) ||
9676 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9677 element == EL_EXPANDABLE_WALL) &&
9678 ((links_massiv && rechts_massiv) ||
9679 element == EL_EXPANDABLE_WALL_VERTICAL))
9680 Feld[ax][ay] = EL_WALL;
9683 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9686 void MauerAblegerStahl(int ax, int ay)
9688 int element = Feld[ax][ay];
9689 int graphic = el2img(element);
9690 boolean oben_frei = FALSE, unten_frei = FALSE;
9691 boolean links_frei = FALSE, rechts_frei = FALSE;
9692 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9693 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9694 boolean new_wall = FALSE;
9696 if (IS_ANIMATED(graphic))
9697 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9699 if (!MovDelay[ax][ay]) /* start building new wall */
9700 MovDelay[ax][ay] = 6;
9702 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9705 if (MovDelay[ax][ay])
9709 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9711 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9713 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9715 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9718 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9719 element == EL_EXPANDABLE_STEELWALL_ANY)
9723 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9724 Store[ax][ay-1] = element;
9725 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9726 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9727 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9728 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9733 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9734 Store[ax][ay+1] = element;
9735 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9736 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9737 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9738 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9743 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9744 element == EL_EXPANDABLE_STEELWALL_ANY)
9748 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9749 Store[ax-1][ay] = element;
9750 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9751 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9752 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9753 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9759 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9760 Store[ax+1][ay] = element;
9761 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9762 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9763 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9764 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9769 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9771 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9772 unten_massiv = TRUE;
9773 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9774 links_massiv = TRUE;
9775 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9776 rechts_massiv = TRUE;
9778 if (((oben_massiv && unten_massiv) ||
9779 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9780 ((links_massiv && rechts_massiv) ||
9781 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9782 Feld[ax][ay] = EL_WALL;
9785 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9788 void CheckForDragon(int x, int y)
9791 boolean dragon_found = FALSE;
9792 static int xy[4][2] =
9800 for (i = 0; i < NUM_DIRECTIONS; i++)
9802 for (j = 0; j < 4; j++)
9804 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9806 if (IN_LEV_FIELD(xx, yy) &&
9807 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9809 if (Feld[xx][yy] == EL_DRAGON)
9810 dragon_found = TRUE;
9819 for (i = 0; i < NUM_DIRECTIONS; i++)
9821 for (j = 0; j < 3; j++)
9823 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9825 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9827 Feld[xx][yy] = EL_EMPTY;
9828 DrawLevelField(xx, yy);
9837 static void InitBuggyBase(int x, int y)
9839 int element = Feld[x][y];
9840 int activating_delay = FRAMES_PER_SECOND / 4;
9843 (element == EL_SP_BUGGY_BASE ?
9844 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9845 element == EL_SP_BUGGY_BASE_ACTIVATING ?
9847 element == EL_SP_BUGGY_BASE_ACTIVE ?
9848 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9851 static void WarnBuggyBase(int x, int y)
9854 static int xy[4][2] =
9862 for (i = 0; i < NUM_DIRECTIONS; i++)
9864 int xx = x + xy[i][0];
9865 int yy = y + xy[i][1];
9867 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9869 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9876 static void InitTrap(int x, int y)
9878 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9881 static void ActivateTrap(int x, int y)
9883 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9886 static void ChangeActiveTrap(int x, int y)
9888 int graphic = IMG_TRAP_ACTIVE;
9890 /* if new animation frame was drawn, correct crumbled sand border */
9891 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9892 DrawLevelFieldCrumbledSand(x, y);
9895 static int getSpecialActionElement(int element, int number, int base_element)
9897 return (element != EL_EMPTY ? element :
9898 number != -1 ? base_element + number - 1 :
9902 static int getModifiedActionNumber(int value_old, int operator, int operand,
9903 int value_min, int value_max)
9905 int value_new = (operator == CA_MODE_SET ? operand :
9906 operator == CA_MODE_ADD ? value_old + operand :
9907 operator == CA_MODE_SUBTRACT ? value_old - operand :
9908 operator == CA_MODE_MULTIPLY ? value_old * operand :
9909 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
9910 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
9913 return (value_new < value_min ? value_min :
9914 value_new > value_max ? value_max :
9918 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9920 struct ElementInfo *ei = &element_info[element];
9921 struct ElementChangeInfo *change = &ei->change_page[page];
9922 int target_element = change->target_element;
9923 int action_type = change->action_type;
9924 int action_mode = change->action_mode;
9925 int action_arg = change->action_arg;
9928 if (!change->has_action)
9931 /* ---------- determine action paramater values -------------------------- */
9933 int level_time_value =
9934 (level.time > 0 ? TimeLeft :
9937 int action_arg_element =
9938 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
9939 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9940 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
9943 int action_arg_direction =
9944 (action_arg >= CA_ARG_DIRECTION_LEFT &&
9945 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9946 action_arg == CA_ARG_DIRECTION_TRIGGER ?
9947 change->actual_trigger_side :
9948 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9949 MV_DIR_OPPOSITE(change->actual_trigger_side) :
9952 int action_arg_number_min =
9953 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9956 int action_arg_number_max =
9957 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9958 action_type == CA_SET_LEVEL_GEMS ? 999 :
9959 action_type == CA_SET_LEVEL_TIME ? 9999 :
9960 action_type == CA_SET_LEVEL_SCORE ? 99999 :
9961 action_type == CA_SET_CE_VALUE ? 9999 :
9962 action_type == CA_SET_CE_SCORE ? 9999 :
9965 int action_arg_number_reset =
9966 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9967 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9968 action_type == CA_SET_LEVEL_TIME ? level.time :
9969 action_type == CA_SET_LEVEL_SCORE ? 0 :
9970 #if USE_NEW_CUSTOM_VALUE
9971 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9973 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
9975 action_type == CA_SET_CE_SCORE ? 0 :
9978 int action_arg_number =
9979 (action_arg <= CA_ARG_MAX ? action_arg :
9980 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9981 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9982 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9983 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9984 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9985 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9986 #if USE_NEW_CUSTOM_VALUE
9987 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9989 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
9991 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9992 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9993 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9994 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9995 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9996 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9997 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9998 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9999 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10000 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
10001 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10004 int action_arg_number_old =
10005 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10006 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10007 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10008 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10009 action_type == CA_SET_CE_SCORE ? ei->collect_score :
10012 int action_arg_number_new =
10013 getModifiedActionNumber(action_arg_number_old,
10014 action_mode, action_arg_number,
10015 action_arg_number_min, action_arg_number_max);
10018 int trigger_player_bits = change->actual_trigger_player_bits;
10020 int trigger_player_bits =
10021 (change->actual_trigger_player >= EL_PLAYER_1 &&
10022 change->actual_trigger_player <= EL_PLAYER_4 ?
10023 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10027 int action_arg_player_bits =
10028 (action_arg >= CA_ARG_PLAYER_1 &&
10029 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10030 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10033 /* ---------- execute action -------------------------------------------- */
10035 switch (action_type)
10042 /* ---------- level actions ------------------------------------------- */
10044 case CA_RESTART_LEVEL:
10046 game.restart_level = TRUE;
10051 case CA_SHOW_ENVELOPE:
10053 int element = getSpecialActionElement(action_arg_element,
10054 action_arg_number, EL_ENVELOPE_1);
10056 if (IS_ENVELOPE(element))
10057 local_player->show_envelope = element;
10062 case CA_SET_LEVEL_TIME:
10064 if (level.time > 0) /* only modify limited time value */
10066 TimeLeft = action_arg_number_new;
10069 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10071 DisplayGameControlValues();
10073 DrawGameValue_Time(TimeLeft);
10076 if (!TimeLeft && setup.time_limit)
10077 for (i = 0; i < MAX_PLAYERS; i++)
10078 KillPlayer(&stored_player[i]);
10084 case CA_SET_LEVEL_SCORE:
10086 local_player->score = action_arg_number_new;
10089 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10091 DisplayGameControlValues();
10093 DrawGameValue_Score(local_player->score);
10099 case CA_SET_LEVEL_GEMS:
10101 local_player->gems_still_needed = action_arg_number_new;
10104 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10106 DisplayGameControlValues();
10108 DrawGameValue_Emeralds(local_player->gems_still_needed);
10114 #if !USE_PLAYER_GRAVITY
10115 case CA_SET_LEVEL_GRAVITY:
10117 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10118 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10119 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10125 case CA_SET_LEVEL_WIND:
10127 game.wind_direction = action_arg_direction;
10132 /* ---------- player actions ------------------------------------------ */
10134 case CA_MOVE_PLAYER:
10136 /* automatically move to the next field in specified direction */
10137 for (i = 0; i < MAX_PLAYERS; i++)
10138 if (trigger_player_bits & (1 << i))
10139 stored_player[i].programmed_action = action_arg_direction;
10144 case CA_EXIT_PLAYER:
10146 for (i = 0; i < MAX_PLAYERS; i++)
10147 if (action_arg_player_bits & (1 << i))
10148 PlayerWins(&stored_player[i]);
10153 case CA_KILL_PLAYER:
10155 for (i = 0; i < MAX_PLAYERS; i++)
10156 if (action_arg_player_bits & (1 << i))
10157 KillPlayer(&stored_player[i]);
10162 case CA_SET_PLAYER_KEYS:
10164 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10165 int element = getSpecialActionElement(action_arg_element,
10166 action_arg_number, EL_KEY_1);
10168 if (IS_KEY(element))
10170 for (i = 0; i < MAX_PLAYERS; i++)
10172 if (trigger_player_bits & (1 << i))
10174 stored_player[i].key[KEY_NR(element)] = key_state;
10176 DrawGameDoorValues();
10184 case CA_SET_PLAYER_SPEED:
10186 for (i = 0; i < MAX_PLAYERS; i++)
10188 if (trigger_player_bits & (1 << i))
10190 int move_stepsize = TILEX / stored_player[i].move_delay_value;
10192 if (action_arg == CA_ARG_SPEED_FASTER &&
10193 stored_player[i].cannot_move)
10195 action_arg_number = STEPSIZE_VERY_SLOW;
10197 else if (action_arg == CA_ARG_SPEED_SLOWER ||
10198 action_arg == CA_ARG_SPEED_FASTER)
10200 action_arg_number = 2;
10201 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10204 else if (action_arg == CA_ARG_NUMBER_RESET)
10206 action_arg_number = level.initial_player_stepsize[i];
10210 getModifiedActionNumber(move_stepsize,
10213 action_arg_number_min,
10214 action_arg_number_max);
10216 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10223 case CA_SET_PLAYER_SHIELD:
10225 for (i = 0; i < MAX_PLAYERS; i++)
10227 if (trigger_player_bits & (1 << i))
10229 if (action_arg == CA_ARG_SHIELD_OFF)
10231 stored_player[i].shield_normal_time_left = 0;
10232 stored_player[i].shield_deadly_time_left = 0;
10234 else if (action_arg == CA_ARG_SHIELD_NORMAL)
10236 stored_player[i].shield_normal_time_left = 999999;
10238 else if (action_arg == CA_ARG_SHIELD_DEADLY)
10240 stored_player[i].shield_normal_time_left = 999999;
10241 stored_player[i].shield_deadly_time_left = 999999;
10249 #if USE_PLAYER_GRAVITY
10250 case CA_SET_PLAYER_GRAVITY:
10252 for (i = 0; i < MAX_PLAYERS; i++)
10254 if (trigger_player_bits & (1 << i))
10256 stored_player[i].gravity =
10257 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10258 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10259 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10260 stored_player[i].gravity);
10268 case CA_SET_PLAYER_ARTWORK:
10270 for (i = 0; i < MAX_PLAYERS; i++)
10272 if (trigger_player_bits & (1 << i))
10274 int artwork_element = action_arg_element;
10276 if (action_arg == CA_ARG_ELEMENT_RESET)
10278 (level.use_artwork_element[i] ? level.artwork_element[i] :
10279 stored_player[i].element_nr);
10281 #if USE_GFX_RESET_PLAYER_ARTWORK
10282 if (stored_player[i].artwork_element != artwork_element)
10283 stored_player[i].Frame = 0;
10286 stored_player[i].artwork_element = artwork_element;
10288 SetPlayerWaiting(&stored_player[i], FALSE);
10290 /* set number of special actions for bored and sleeping animation */
10291 stored_player[i].num_special_action_bored =
10292 get_num_special_action(artwork_element,
10293 ACTION_BORING_1, ACTION_BORING_LAST);
10294 stored_player[i].num_special_action_sleeping =
10295 get_num_special_action(artwork_element,
10296 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10303 /* ---------- CE actions ---------------------------------------------- */
10305 case CA_SET_CE_VALUE:
10307 #if USE_NEW_CUSTOM_VALUE
10308 int last_ce_value = CustomValue[x][y];
10310 CustomValue[x][y] = action_arg_number_new;
10312 if (CustomValue[x][y] != last_ce_value)
10314 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10315 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10317 if (CustomValue[x][y] == 0)
10319 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10320 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10328 case CA_SET_CE_SCORE:
10330 #if USE_NEW_CUSTOM_VALUE
10331 int last_ce_score = ei->collect_score;
10333 ei->collect_score = action_arg_number_new;
10335 if (ei->collect_score != last_ce_score)
10337 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10338 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10340 if (ei->collect_score == 0)
10344 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10345 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10348 This is a very special case that seems to be a mixture between
10349 CheckElementChange() and CheckTriggeredElementChange(): while
10350 the first one only affects single elements that are triggered
10351 directly, the second one affects multiple elements in the playfield
10352 that are triggered indirectly by another element. This is a third
10353 case: Changing the CE score always affects multiple identical CEs,
10354 so every affected CE must be checked, not only the single CE for
10355 which the CE score was changed in the first place (as every instance
10356 of that CE shares the same CE score, and therefore also can change)!
10358 SCAN_PLAYFIELD(xx, yy)
10360 if (Feld[xx][yy] == element)
10361 CheckElementChange(xx, yy, element, EL_UNDEFINED,
10362 CE_SCORE_GETS_ZERO);
10371 /* ---------- engine actions ------------------------------------------ */
10373 case CA_SET_ENGINE_SCAN_MODE:
10375 InitPlayfieldScanMode(action_arg);
10385 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10387 int old_element = Feld[x][y];
10388 int new_element = GetElementFromGroupElement(element);
10389 int previous_move_direction = MovDir[x][y];
10390 #if USE_NEW_CUSTOM_VALUE
10391 int last_ce_value = CustomValue[x][y];
10393 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10394 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10395 boolean add_player_onto_element = (new_element_is_player &&
10396 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
10397 /* this breaks SnakeBite when a snake is
10398 halfway through a door that closes */
10399 /* NOW FIXED AT LEVEL INIT IN files.c */
10400 new_element != EL_SOKOBAN_FIELD_PLAYER &&
10402 IS_WALKABLE(old_element));
10405 /* check if element under the player changes from accessible to unaccessible
10406 (needed for special case of dropping element which then changes) */
10407 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10408 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10416 if (!add_player_onto_element)
10418 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10419 RemoveMovingField(x, y);
10423 Feld[x][y] = new_element;
10425 #if !USE_GFX_RESET_GFX_ANIMATION
10426 ResetGfxAnimation(x, y);
10427 ResetRandomAnimationValue(x, y);
10430 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10431 MovDir[x][y] = previous_move_direction;
10433 #if USE_NEW_CUSTOM_VALUE
10434 if (element_info[new_element].use_last_ce_value)
10435 CustomValue[x][y] = last_ce_value;
10438 InitField_WithBug1(x, y, FALSE);
10440 new_element = Feld[x][y]; /* element may have changed */
10442 #if USE_GFX_RESET_GFX_ANIMATION
10443 ResetGfxAnimation(x, y);
10444 ResetRandomAnimationValue(x, y);
10447 DrawLevelField(x, y);
10449 if (GFX_CRUMBLED(new_element))
10450 DrawLevelFieldCrumbledSandNeighbours(x, y);
10454 /* check if element under the player changes from accessible to unaccessible
10455 (needed for special case of dropping element which then changes) */
10456 /* (must be checked after creating new element for walkable group elements) */
10457 #if USE_FIX_KILLED_BY_NON_WALKABLE
10458 if (IS_PLAYER(x, y) && !player_explosion_protected &&
10459 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10466 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10467 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10476 /* "ChangeCount" not set yet to allow "entered by player" change one time */
10477 if (new_element_is_player)
10478 RelocatePlayer(x, y, new_element);
10481 ChangeCount[x][y]++; /* count number of changes in the same frame */
10483 TestIfBadThingTouchesPlayer(x, y);
10484 TestIfPlayerTouchesCustomElement(x, y);
10485 TestIfElementTouchesCustomElement(x, y);
10488 static void CreateField(int x, int y, int element)
10490 CreateFieldExt(x, y, element, FALSE);
10493 static void CreateElementFromChange(int x, int y, int element)
10495 element = GET_VALID_RUNTIME_ELEMENT(element);
10497 #if USE_STOP_CHANGED_ELEMENTS
10498 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10500 int old_element = Feld[x][y];
10502 /* prevent changed element from moving in same engine frame
10503 unless both old and new element can either fall or move */
10504 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10505 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10510 CreateFieldExt(x, y, element, TRUE);
10513 static boolean ChangeElement(int x, int y, int element, int page)
10515 struct ElementInfo *ei = &element_info[element];
10516 struct ElementChangeInfo *change = &ei->change_page[page];
10517 int ce_value = CustomValue[x][y];
10518 int ce_score = ei->collect_score;
10519 int target_element;
10520 int old_element = Feld[x][y];
10522 /* always use default change event to prevent running into a loop */
10523 if (ChangeEvent[x][y] == -1)
10524 ChangeEvent[x][y] = CE_DELAY;
10526 if (ChangeEvent[x][y] == CE_DELAY)
10528 /* reset actual trigger element, trigger player and action element */
10529 change->actual_trigger_element = EL_EMPTY;
10530 change->actual_trigger_player = EL_PLAYER_1;
10531 change->actual_trigger_player_bits = CH_PLAYER_1;
10532 change->actual_trigger_side = CH_SIDE_NONE;
10533 change->actual_trigger_ce_value = 0;
10534 change->actual_trigger_ce_score = 0;
10537 /* do not change elements more than a specified maximum number of changes */
10538 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10541 ChangeCount[x][y]++; /* count number of changes in the same frame */
10543 if (change->explode)
10550 if (change->use_target_content)
10552 boolean complete_replace = TRUE;
10553 boolean can_replace[3][3];
10556 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10559 boolean is_walkable;
10560 boolean is_diggable;
10561 boolean is_collectible;
10562 boolean is_removable;
10563 boolean is_destructible;
10564 int ex = x + xx - 1;
10565 int ey = y + yy - 1;
10566 int content_element = change->target_content.e[xx][yy];
10569 can_replace[xx][yy] = TRUE;
10571 if (ex == x && ey == y) /* do not check changing element itself */
10574 if (content_element == EL_EMPTY_SPACE)
10576 can_replace[xx][yy] = FALSE; /* do not replace border with space */
10581 if (!IN_LEV_FIELD(ex, ey))
10583 can_replace[xx][yy] = FALSE;
10584 complete_replace = FALSE;
10591 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10592 e = MovingOrBlocked2Element(ex, ey);
10594 is_empty = (IS_FREE(ex, ey) ||
10595 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10597 is_walkable = (is_empty || IS_WALKABLE(e));
10598 is_diggable = (is_empty || IS_DIGGABLE(e));
10599 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10600 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10601 is_removable = (is_diggable || is_collectible);
10603 can_replace[xx][yy] =
10604 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10605 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10606 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10607 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10608 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10609 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10610 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10612 if (!can_replace[xx][yy])
10613 complete_replace = FALSE;
10616 if (!change->only_if_complete || complete_replace)
10618 boolean something_has_changed = FALSE;
10620 if (change->only_if_complete && change->use_random_replace &&
10621 RND(100) < change->random_percentage)
10624 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10626 int ex = x + xx - 1;
10627 int ey = y + yy - 1;
10628 int content_element;
10630 if (can_replace[xx][yy] && (!change->use_random_replace ||
10631 RND(100) < change->random_percentage))
10633 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10634 RemoveMovingField(ex, ey);
10636 ChangeEvent[ex][ey] = ChangeEvent[x][y];
10638 content_element = change->target_content.e[xx][yy];
10639 target_element = GET_TARGET_ELEMENT(element, content_element, change,
10640 ce_value, ce_score);
10642 CreateElementFromChange(ex, ey, target_element);
10644 something_has_changed = TRUE;
10646 /* for symmetry reasons, freeze newly created border elements */
10647 if (ex != x || ey != y)
10648 Stop[ex][ey] = TRUE; /* no more moving in this frame */
10652 if (something_has_changed)
10654 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10655 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10661 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10662 ce_value, ce_score);
10664 if (element == EL_DIAGONAL_GROWING ||
10665 element == EL_DIAGONAL_SHRINKING)
10667 target_element = Store[x][y];
10669 Store[x][y] = EL_EMPTY;
10672 CreateElementFromChange(x, y, target_element);
10674 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10675 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10678 /* this uses direct change before indirect change */
10679 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10684 #if USE_NEW_DELAYED_ACTION
10686 static void HandleElementChange(int x, int y, int page)
10688 int element = MovingOrBlocked2Element(x, y);
10689 struct ElementInfo *ei = &element_info[element];
10690 struct ElementChangeInfo *change = &ei->change_page[page];
10693 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10694 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10697 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10698 x, y, element, element_info[element].token_name);
10699 printf("HandleElementChange(): This should never happen!\n");
10704 /* this can happen with classic bombs on walkable, changing elements */
10705 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10708 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
10709 ChangeDelay[x][y] = 0;
10715 if (ChangeDelay[x][y] == 0) /* initialize element change */
10717 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10719 if (change->can_change)
10722 /* !!! not clear why graphic animation should be reset at all here !!! */
10723 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10724 #if USE_GFX_RESET_WHEN_NOT_MOVING
10725 /* when a custom element is about to change (for example by change delay),
10726 do not reset graphic animation when the custom element is moving */
10727 if (!IS_MOVING(x, y))
10730 ResetGfxAnimation(x, y);
10731 ResetRandomAnimationValue(x, y);
10735 if (change->pre_change_function)
10736 change->pre_change_function(x, y);
10740 ChangeDelay[x][y]--;
10742 if (ChangeDelay[x][y] != 0) /* continue element change */
10744 if (change->can_change)
10746 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10748 if (IS_ANIMATED(graphic))
10749 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10751 if (change->change_function)
10752 change->change_function(x, y);
10755 else /* finish element change */
10757 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10759 page = ChangePage[x][y];
10760 ChangePage[x][y] = -1;
10762 change = &ei->change_page[page];
10765 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10767 ChangeDelay[x][y] = 1; /* try change after next move step */
10768 ChangePage[x][y] = page; /* remember page to use for change */
10773 if (change->can_change)
10775 if (ChangeElement(x, y, element, page))
10777 if (change->post_change_function)
10778 change->post_change_function(x, y);
10782 if (change->has_action)
10783 ExecuteCustomElementAction(x, y, element, page);
10789 static void HandleElementChange(int x, int y, int page)
10791 int element = MovingOrBlocked2Element(x, y);
10792 struct ElementInfo *ei = &element_info[element];
10793 struct ElementChangeInfo *change = &ei->change_page[page];
10796 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
10799 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10800 x, y, element, element_info[element].token_name);
10801 printf("HandleElementChange(): This should never happen!\n");
10806 /* this can happen with classic bombs on walkable, changing elements */
10807 if (!CAN_CHANGE(element))
10810 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
10811 ChangeDelay[x][y] = 0;
10817 if (ChangeDelay[x][y] == 0) /* initialize element change */
10819 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10821 ResetGfxAnimation(x, y);
10822 ResetRandomAnimationValue(x, y);
10824 if (change->pre_change_function)
10825 change->pre_change_function(x, y);
10828 ChangeDelay[x][y]--;
10830 if (ChangeDelay[x][y] != 0) /* continue element change */
10832 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10834 if (IS_ANIMATED(graphic))
10835 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10837 if (change->change_function)
10838 change->change_function(x, y);
10840 else /* finish element change */
10842 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10844 page = ChangePage[x][y];
10845 ChangePage[x][y] = -1;
10847 change = &ei->change_page[page];
10850 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10852 ChangeDelay[x][y] = 1; /* try change after next move step */
10853 ChangePage[x][y] = page; /* remember page to use for change */
10858 if (ChangeElement(x, y, element, page))
10860 if (change->post_change_function)
10861 change->post_change_function(x, y);
10868 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10869 int trigger_element,
10871 int trigger_player,
10875 boolean change_done_any = FALSE;
10876 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10879 if (!(trigger_events[trigger_element][trigger_event]))
10883 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10884 trigger_event, recursion_loop_depth, recursion_loop_detected,
10885 recursion_loop_element, EL_NAME(recursion_loop_element));
10888 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10890 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10892 int element = EL_CUSTOM_START + i;
10893 boolean change_done = FALSE;
10896 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10897 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10900 for (p = 0; p < element_info[element].num_change_pages; p++)
10902 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10904 if (change->can_change_or_has_action &&
10905 change->has_event[trigger_event] &&
10906 change->trigger_side & trigger_side &&
10907 change->trigger_player & trigger_player &&
10908 change->trigger_page & trigger_page_bits &&
10909 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10911 change->actual_trigger_element = trigger_element;
10912 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10913 change->actual_trigger_player_bits = trigger_player;
10914 change->actual_trigger_side = trigger_side;
10915 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10916 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10918 if ((change->can_change && !change_done) || change->has_action)
10922 SCAN_PLAYFIELD(x, y)
10924 if (Feld[x][y] == element)
10926 if (change->can_change && !change_done)
10928 ChangeDelay[x][y] = 1;
10929 ChangeEvent[x][y] = trigger_event;
10931 HandleElementChange(x, y, p);
10933 #if USE_NEW_DELAYED_ACTION
10934 else if (change->has_action)
10936 ExecuteCustomElementAction(x, y, element, p);
10937 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10940 if (change->has_action)
10942 ExecuteCustomElementAction(x, y, element, p);
10943 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10949 if (change->can_change)
10951 change_done = TRUE;
10952 change_done_any = TRUE;
10959 RECURSION_LOOP_DETECTION_END();
10961 return change_done_any;
10964 static boolean CheckElementChangeExt(int x, int y,
10966 int trigger_element,
10968 int trigger_player,
10971 boolean change_done = FALSE;
10974 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10975 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10978 if (Feld[x][y] == EL_BLOCKED)
10980 Blocked2Moving(x, y, &x, &y);
10981 element = Feld[x][y];
10985 /* check if element has already changed */
10986 if (Feld[x][y] != element)
10989 /* check if element has already changed or is about to change after moving */
10990 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10991 Feld[x][y] != element) ||
10993 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10994 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10995 ChangePage[x][y] != -1)))
11000 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11001 trigger_event, recursion_loop_depth, recursion_loop_detected,
11002 recursion_loop_element, EL_NAME(recursion_loop_element));
11005 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11007 for (p = 0; p < element_info[element].num_change_pages; p++)
11009 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11011 /* check trigger element for all events where the element that is checked
11012 for changing interacts with a directly adjacent element -- this is
11013 different to element changes that affect other elements to change on the
11014 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11015 boolean check_trigger_element =
11016 (trigger_event == CE_TOUCHING_X ||
11017 trigger_event == CE_HITTING_X ||
11018 trigger_event == CE_HIT_BY_X ||
11020 /* this one was forgotten until 3.2.3 */
11021 trigger_event == CE_DIGGING_X);
11024 if (change->can_change_or_has_action &&
11025 change->has_event[trigger_event] &&
11026 change->trigger_side & trigger_side &&
11027 change->trigger_player & trigger_player &&
11028 (!check_trigger_element ||
11029 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11031 change->actual_trigger_element = trigger_element;
11032 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11033 change->actual_trigger_player_bits = trigger_player;
11034 change->actual_trigger_side = trigger_side;
11035 change->actual_trigger_ce_value = CustomValue[x][y];
11036 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11038 /* special case: trigger element not at (x,y) position for some events */
11039 if (check_trigger_element)
11051 { 0, 0 }, { 0, 0 }, { 0, 0 },
11055 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11056 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11058 change->actual_trigger_ce_value = CustomValue[xx][yy];
11059 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11062 if (change->can_change && !change_done)
11064 ChangeDelay[x][y] = 1;
11065 ChangeEvent[x][y] = trigger_event;
11067 HandleElementChange(x, y, p);
11069 change_done = TRUE;
11071 #if USE_NEW_DELAYED_ACTION
11072 else if (change->has_action)
11074 ExecuteCustomElementAction(x, y, element, p);
11075 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11078 if (change->has_action)
11080 ExecuteCustomElementAction(x, y, element, p);
11081 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11087 RECURSION_LOOP_DETECTION_END();
11089 return change_done;
11092 static void PlayPlayerSound(struct PlayerInfo *player)
11094 int jx = player->jx, jy = player->jy;
11095 int sound_element = player->artwork_element;
11096 int last_action = player->last_action_waiting;
11097 int action = player->action_waiting;
11099 if (player->is_waiting)
11101 if (action != last_action)
11102 PlayLevelSoundElementAction(jx, jy, sound_element, action);
11104 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11108 if (action != last_action)
11109 StopSound(element_info[sound_element].sound[last_action]);
11111 if (last_action == ACTION_SLEEPING)
11112 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11116 static void PlayAllPlayersSound()
11120 for (i = 0; i < MAX_PLAYERS; i++)
11121 if (stored_player[i].active)
11122 PlayPlayerSound(&stored_player[i]);
11125 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11127 boolean last_waiting = player->is_waiting;
11128 int move_dir = player->MovDir;
11130 player->dir_waiting = move_dir;
11131 player->last_action_waiting = player->action_waiting;
11135 if (!last_waiting) /* not waiting -> waiting */
11137 player->is_waiting = TRUE;
11139 player->frame_counter_bored =
11141 game.player_boring_delay_fixed +
11142 GetSimpleRandom(game.player_boring_delay_random);
11143 player->frame_counter_sleeping =
11145 game.player_sleeping_delay_fixed +
11146 GetSimpleRandom(game.player_sleeping_delay_random);
11148 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11151 if (game.player_sleeping_delay_fixed +
11152 game.player_sleeping_delay_random > 0 &&
11153 player->anim_delay_counter == 0 &&
11154 player->post_delay_counter == 0 &&
11155 FrameCounter >= player->frame_counter_sleeping)
11156 player->is_sleeping = TRUE;
11157 else if (game.player_boring_delay_fixed +
11158 game.player_boring_delay_random > 0 &&
11159 FrameCounter >= player->frame_counter_bored)
11160 player->is_bored = TRUE;
11162 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11163 player->is_bored ? ACTION_BORING :
11166 if (player->is_sleeping && player->use_murphy)
11168 /* special case for sleeping Murphy when leaning against non-free tile */
11170 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11171 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11172 !IS_MOVING(player->jx - 1, player->jy)))
11173 move_dir = MV_LEFT;
11174 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11175 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11176 !IS_MOVING(player->jx + 1, player->jy)))
11177 move_dir = MV_RIGHT;
11179 player->is_sleeping = FALSE;
11181 player->dir_waiting = move_dir;
11184 if (player->is_sleeping)
11186 if (player->num_special_action_sleeping > 0)
11188 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11190 int last_special_action = player->special_action_sleeping;
11191 int num_special_action = player->num_special_action_sleeping;
11192 int special_action =
11193 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11194 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11195 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11196 last_special_action + 1 : ACTION_SLEEPING);
11197 int special_graphic =
11198 el_act_dir2img(player->artwork_element, special_action, move_dir);
11200 player->anim_delay_counter =
11201 graphic_info[special_graphic].anim_delay_fixed +
11202 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11203 player->post_delay_counter =
11204 graphic_info[special_graphic].post_delay_fixed +
11205 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11207 player->special_action_sleeping = special_action;
11210 if (player->anim_delay_counter > 0)
11212 player->action_waiting = player->special_action_sleeping;
11213 player->anim_delay_counter--;
11215 else if (player->post_delay_counter > 0)
11217 player->post_delay_counter--;
11221 else if (player->is_bored)
11223 if (player->num_special_action_bored > 0)
11225 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11227 int special_action =
11228 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11229 int special_graphic =
11230 el_act_dir2img(player->artwork_element, special_action, move_dir);
11232 player->anim_delay_counter =
11233 graphic_info[special_graphic].anim_delay_fixed +
11234 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11235 player->post_delay_counter =
11236 graphic_info[special_graphic].post_delay_fixed +
11237 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11239 player->special_action_bored = special_action;
11242 if (player->anim_delay_counter > 0)
11244 player->action_waiting = player->special_action_bored;
11245 player->anim_delay_counter--;
11247 else if (player->post_delay_counter > 0)
11249 player->post_delay_counter--;
11254 else if (last_waiting) /* waiting -> not waiting */
11256 player->is_waiting = FALSE;
11257 player->is_bored = FALSE;
11258 player->is_sleeping = FALSE;
11260 player->frame_counter_bored = -1;
11261 player->frame_counter_sleeping = -1;
11263 player->anim_delay_counter = 0;
11264 player->post_delay_counter = 0;
11266 player->dir_waiting = player->MovDir;
11267 player->action_waiting = ACTION_DEFAULT;
11269 player->special_action_bored = ACTION_DEFAULT;
11270 player->special_action_sleeping = ACTION_DEFAULT;
11274 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11276 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11277 int left = player_action & JOY_LEFT;
11278 int right = player_action & JOY_RIGHT;
11279 int up = player_action & JOY_UP;
11280 int down = player_action & JOY_DOWN;
11281 int button1 = player_action & JOY_BUTTON_1;
11282 int button2 = player_action & JOY_BUTTON_2;
11283 int dx = (left ? -1 : right ? 1 : 0);
11284 int dy = (up ? -1 : down ? 1 : 0);
11286 if (!player->active || tape.pausing)
11292 snapped = SnapField(player, dx, dy);
11296 dropped = DropElement(player);
11298 moved = MovePlayer(player, dx, dy);
11301 if (tape.single_step && tape.recording && !tape.pausing)
11303 if (button1 || (dropped && !moved))
11305 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11306 SnapField(player, 0, 0); /* stop snapping */
11310 SetPlayerWaiting(player, FALSE);
11312 return player_action;
11316 /* no actions for this player (no input at player's configured device) */
11318 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11319 SnapField(player, 0, 0);
11320 CheckGravityMovementWhenNotMoving(player);
11322 if (player->MovPos == 0)
11323 SetPlayerWaiting(player, TRUE);
11325 if (player->MovPos == 0) /* needed for tape.playing */
11326 player->is_moving = FALSE;
11328 player->is_dropping = FALSE;
11329 player->is_dropping_pressed = FALSE;
11330 player->drop_pressed_delay = 0;
11336 static void CheckLevelTime()
11340 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11342 if (level.native_em_level->lev->home == 0) /* all players at home */
11344 PlayerWins(local_player);
11346 AllPlayersGone = TRUE;
11348 level.native_em_level->lev->home = -1;
11351 if (level.native_em_level->ply[0]->alive == 0 &&
11352 level.native_em_level->ply[1]->alive == 0 &&
11353 level.native_em_level->ply[2]->alive == 0 &&
11354 level.native_em_level->ply[3]->alive == 0) /* all dead */
11355 AllPlayersGone = TRUE;
11358 if (TimeFrames >= FRAMES_PER_SECOND)
11363 for (i = 0; i < MAX_PLAYERS; i++)
11365 struct PlayerInfo *player = &stored_player[i];
11367 if (SHIELD_ON(player))
11369 player->shield_normal_time_left--;
11371 if (player->shield_deadly_time_left > 0)
11372 player->shield_deadly_time_left--;
11376 if (!local_player->LevelSolved && !level.use_step_counter)
11384 if (TimeLeft <= 10 && setup.time_limit)
11385 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11388 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11390 DisplayGameControlValues();
11392 DrawGameValue_Time(TimeLeft);
11395 if (!TimeLeft && setup.time_limit)
11397 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11398 level.native_em_level->lev->killed_out_of_time = TRUE;
11400 for (i = 0; i < MAX_PLAYERS; i++)
11401 KillPlayer(&stored_player[i]);
11405 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11407 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11409 DisplayGameControlValues();
11412 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11413 DrawGameValue_Time(TimePlayed);
11416 level.native_em_level->lev->time =
11417 (level.time == 0 ? TimePlayed : TimeLeft);
11420 if (tape.recording || tape.playing)
11421 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11424 UpdateGameDoorValues();
11425 DrawGameDoorValues();
11428 void AdvanceFrameAndPlayerCounters(int player_nr)
11432 /* advance frame counters (global frame counter and time frame counter) */
11436 /* advance player counters (counters for move delay, move animation etc.) */
11437 for (i = 0; i < MAX_PLAYERS; i++)
11439 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11440 int move_delay_value = stored_player[i].move_delay_value;
11441 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11443 if (!advance_player_counters) /* not all players may be affected */
11446 #if USE_NEW_PLAYER_ANIM
11447 if (move_frames == 0) /* less than one move per game frame */
11449 int stepsize = TILEX / move_delay_value;
11450 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11451 int count = (stored_player[i].is_moving ?
11452 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11454 if (count % delay == 0)
11459 stored_player[i].Frame += move_frames;
11461 if (stored_player[i].MovPos != 0)
11462 stored_player[i].StepFrame += move_frames;
11464 if (stored_player[i].move_delay > 0)
11465 stored_player[i].move_delay--;
11467 /* due to bugs in previous versions, counter must count up, not down */
11468 if (stored_player[i].push_delay != -1)
11469 stored_player[i].push_delay++;
11471 if (stored_player[i].drop_delay > 0)
11472 stored_player[i].drop_delay--;
11474 if (stored_player[i].is_dropping_pressed)
11475 stored_player[i].drop_pressed_delay++;
11479 void StartGameActions(boolean init_network_game, boolean record_tape,
11482 unsigned long new_random_seed = InitRND(random_seed);
11485 TapeStartRecording(new_random_seed);
11487 #if defined(NETWORK_AVALIABLE)
11488 if (init_network_game)
11490 SendToServer_StartPlaying();
11501 static unsigned long game_frame_delay = 0;
11502 unsigned long game_frame_delay_value;
11503 byte *recorded_player_action;
11504 byte summarized_player_action = 0;
11505 byte tape_action[MAX_PLAYERS];
11508 /* detect endless loops, caused by custom element programming */
11509 if (recursion_loop_detected && recursion_loop_depth == 0)
11511 char *message = getStringCat3("Internal Error ! Element ",
11512 EL_NAME(recursion_loop_element),
11513 " caused endless loop ! Quit the game ?");
11515 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11516 EL_NAME(recursion_loop_element));
11518 RequestQuitGameExt(FALSE, level_editor_test_game, message);
11520 recursion_loop_detected = FALSE; /* if game should be continued */
11527 if (game.restart_level)
11528 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
11530 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11532 if (level.native_em_level->lev->home == 0) /* all players at home */
11534 PlayerWins(local_player);
11536 AllPlayersGone = TRUE;
11538 level.native_em_level->lev->home = -1;
11541 if (level.native_em_level->ply[0]->alive == 0 &&
11542 level.native_em_level->ply[1]->alive == 0 &&
11543 level.native_em_level->ply[2]->alive == 0 &&
11544 level.native_em_level->ply[3]->alive == 0) /* all dead */
11545 AllPlayersGone = TRUE;
11548 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11551 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11554 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
11557 game_frame_delay_value =
11558 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11560 if (tape.playing && tape.warp_forward && !tape.pausing)
11561 game_frame_delay_value = 0;
11563 /* ---------- main game synchronization point ---------- */
11565 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11567 if (network_playing && !network_player_action_received)
11569 /* try to get network player actions in time */
11571 #if defined(NETWORK_AVALIABLE)
11572 /* last chance to get network player actions without main loop delay */
11573 HandleNetworking();
11576 /* game was quit by network peer */
11577 if (game_status != GAME_MODE_PLAYING)
11580 if (!network_player_action_received)
11581 return; /* failed to get network player actions in time */
11583 /* do not yet reset "network_player_action_received" (for tape.pausing) */
11589 /* at this point we know that we really continue executing the game */
11591 network_player_action_received = FALSE;
11593 /* when playing tape, read previously recorded player input from tape data */
11594 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11597 /* TapePlayAction() may return NULL when toggling to "pause before death" */
11602 if (tape.set_centered_player)
11604 game.centered_player_nr_next = tape.centered_player_nr_next;
11605 game.set_centered_player = TRUE;
11608 for (i = 0; i < MAX_PLAYERS; i++)
11610 summarized_player_action |= stored_player[i].action;
11612 if (!network_playing)
11613 stored_player[i].effective_action = stored_player[i].action;
11616 #if defined(NETWORK_AVALIABLE)
11617 if (network_playing)
11618 SendToServer_MovePlayer(summarized_player_action);
11621 if (!options.network && !setup.team_mode)
11622 local_player->effective_action = summarized_player_action;
11624 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
11626 for (i = 0; i < MAX_PLAYERS; i++)
11627 stored_player[i].effective_action =
11628 (i == game.centered_player_nr ? summarized_player_action : 0);
11631 if (recorded_player_action != NULL)
11632 for (i = 0; i < MAX_PLAYERS; i++)
11633 stored_player[i].effective_action = recorded_player_action[i];
11635 for (i = 0; i < MAX_PLAYERS; i++)
11637 tape_action[i] = stored_player[i].effective_action;
11639 /* (this can only happen in the R'n'D game engine) */
11640 if (tape.recording && tape_action[i] && !tape.player_participates[i])
11641 tape.player_participates[i] = TRUE; /* player just appeared from CE */
11644 /* only record actions from input devices, but not programmed actions */
11645 if (tape.recording)
11646 TapeRecordAction(tape_action);
11648 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11650 GameActions_EM_Main();
11658 void GameActions_EM_Main()
11660 byte effective_action[MAX_PLAYERS];
11661 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11664 for (i = 0; i < MAX_PLAYERS; i++)
11665 effective_action[i] = stored_player[i].effective_action;
11667 GameActions_EM(effective_action, warp_mode);
11671 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11674 void GameActions_RND()
11676 int magic_wall_x = 0, magic_wall_y = 0;
11677 int i, x, y, element, graphic;
11679 InitPlayfieldScanModeVars();
11681 #if USE_ONE_MORE_CHANGE_PER_FRAME
11682 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11684 SCAN_PLAYFIELD(x, y)
11686 ChangeCount[x][y] = 0;
11687 ChangeEvent[x][y] = -1;
11692 if (game.set_centered_player)
11694 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11696 /* switching to "all players" only possible if all players fit to screen */
11697 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11699 game.centered_player_nr_next = game.centered_player_nr;
11700 game.set_centered_player = FALSE;
11703 /* do not switch focus to non-existing (or non-active) player */
11704 if (game.centered_player_nr_next >= 0 &&
11705 !stored_player[game.centered_player_nr_next].active)
11707 game.centered_player_nr_next = game.centered_player_nr;
11708 game.set_centered_player = FALSE;
11712 if (game.set_centered_player &&
11713 ScreenMovPos == 0) /* screen currently aligned at tile position */
11717 if (game.centered_player_nr_next == -1)
11719 setScreenCenteredToAllPlayers(&sx, &sy);
11723 sx = stored_player[game.centered_player_nr_next].jx;
11724 sy = stored_player[game.centered_player_nr_next].jy;
11727 game.centered_player_nr = game.centered_player_nr_next;
11728 game.set_centered_player = FALSE;
11730 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11731 DrawGameDoorValues();
11734 for (i = 0; i < MAX_PLAYERS; i++)
11736 int actual_player_action = stored_player[i].effective_action;
11739 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11740 - rnd_equinox_tetrachloride 048
11741 - rnd_equinox_tetrachloride_ii 096
11742 - rnd_emanuel_schmieg 002
11743 - doctor_sloan_ww 001, 020
11745 if (stored_player[i].MovPos == 0)
11746 CheckGravityMovement(&stored_player[i]);
11749 /* overwrite programmed action with tape action */
11750 if (stored_player[i].programmed_action)
11751 actual_player_action = stored_player[i].programmed_action;
11753 PlayerActions(&stored_player[i], actual_player_action);
11755 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11758 ScrollScreen(NULL, SCROLL_GO_ON);
11760 /* for backwards compatibility, the following code emulates a fixed bug that
11761 occured when pushing elements (causing elements that just made their last
11762 pushing step to already (if possible) make their first falling step in the
11763 same game frame, which is bad); this code is also needed to use the famous
11764 "spring push bug" which is used in older levels and might be wanted to be
11765 used also in newer levels, but in this case the buggy pushing code is only
11766 affecting the "spring" element and no other elements */
11768 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11770 for (i = 0; i < MAX_PLAYERS; i++)
11772 struct PlayerInfo *player = &stored_player[i];
11773 int x = player->jx;
11774 int y = player->jy;
11776 if (player->active && player->is_pushing && player->is_moving &&
11778 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11779 Feld[x][y] == EL_SPRING))
11781 ContinueMoving(x, y);
11783 /* continue moving after pushing (this is actually a bug) */
11784 if (!IS_MOVING(x, y))
11785 Stop[x][y] = FALSE;
11791 debug_print_timestamp(0, "start main loop profiling");
11794 SCAN_PLAYFIELD(x, y)
11796 ChangeCount[x][y] = 0;
11797 ChangeEvent[x][y] = -1;
11799 /* this must be handled before main playfield loop */
11800 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11803 if (MovDelay[x][y] <= 0)
11807 #if USE_NEW_SNAP_DELAY
11808 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11811 if (MovDelay[x][y] <= 0)
11814 DrawLevelField(x, y);
11816 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11822 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11824 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11825 printf("GameActions(): This should never happen!\n");
11827 ChangePage[x][y] = -1;
11831 Stop[x][y] = FALSE;
11832 if (WasJustMoving[x][y] > 0)
11833 WasJustMoving[x][y]--;
11834 if (WasJustFalling[x][y] > 0)
11835 WasJustFalling[x][y]--;
11836 if (CheckCollision[x][y] > 0)
11837 CheckCollision[x][y]--;
11838 if (CheckImpact[x][y] > 0)
11839 CheckImpact[x][y]--;
11843 /* reset finished pushing action (not done in ContinueMoving() to allow
11844 continuous pushing animation for elements with zero push delay) */
11845 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11847 ResetGfxAnimation(x, y);
11848 DrawLevelField(x, y);
11852 if (IS_BLOCKED(x, y))
11856 Blocked2Moving(x, y, &oldx, &oldy);
11857 if (!IS_MOVING(oldx, oldy))
11859 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11860 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11861 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11862 printf("GameActions(): This should never happen!\n");
11869 debug_print_timestamp(0, "- time for pre-main loop:");
11872 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
11873 SCAN_PLAYFIELD(x, y)
11875 element = Feld[x][y];
11876 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11881 int element2 = element;
11882 int graphic2 = graphic;
11884 int element2 = Feld[x][y];
11885 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
11887 int last_gfx_frame = GfxFrame[x][y];
11889 if (graphic_info[graphic2].anim_global_sync)
11890 GfxFrame[x][y] = FrameCounter;
11891 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
11892 GfxFrame[x][y] = CustomValue[x][y];
11893 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
11894 GfxFrame[x][y] = element_info[element2].collect_score;
11895 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
11896 GfxFrame[x][y] = ChangeDelay[x][y];
11898 if (redraw && GfxFrame[x][y] != last_gfx_frame)
11899 DrawLevelGraphicAnimation(x, y, graphic2);
11902 ResetGfxFrame(x, y, TRUE);
11906 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11907 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11908 ResetRandomAnimationValue(x, y);
11912 SetRandomAnimationValue(x, y);
11916 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11919 #endif // -------------------- !!! TEST ONLY !!! --------------------
11922 debug_print_timestamp(0, "- time for TEST loop: -->");
11925 SCAN_PLAYFIELD(x, y)
11927 element = Feld[x][y];
11928 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11930 ResetGfxFrame(x, y, TRUE);
11932 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11933 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11934 ResetRandomAnimationValue(x, y);
11936 SetRandomAnimationValue(x, y);
11938 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11940 if (IS_INACTIVE(element))
11942 if (IS_ANIMATED(graphic))
11943 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11948 /* this may take place after moving, so 'element' may have changed */
11949 if (IS_CHANGING(x, y) &&
11950 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11952 int page = element_info[element].event_page_nr[CE_DELAY];
11955 HandleElementChange(x, y, page);
11957 if (CAN_CHANGE(element))
11958 HandleElementChange(x, y, page);
11960 if (HAS_ACTION(element))
11961 ExecuteCustomElementAction(x, y, element, page);
11964 element = Feld[x][y];
11965 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11968 #if 0 // ---------------------------------------------------------------------
11970 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11974 element = Feld[x][y];
11975 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11977 if (IS_ANIMATED(graphic) &&
11978 !IS_MOVING(x, y) &&
11980 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11982 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11983 DrawTwinkleOnField(x, y);
11985 else if (IS_MOVING(x, y))
11986 ContinueMoving(x, y);
11993 case EL_EM_EXIT_OPEN:
11994 case EL_SP_EXIT_OPEN:
11995 case EL_STEEL_EXIT_OPEN:
11996 case EL_EM_STEEL_EXIT_OPEN:
11997 case EL_SP_TERMINAL:
11998 case EL_SP_TERMINAL_ACTIVE:
11999 case EL_EXTRA_TIME:
12000 case EL_SHIELD_NORMAL:
12001 case EL_SHIELD_DEADLY:
12002 if (IS_ANIMATED(graphic))
12003 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12006 case EL_DYNAMITE_ACTIVE:
12007 case EL_EM_DYNAMITE_ACTIVE:
12008 case EL_DYNABOMB_PLAYER_1_ACTIVE:
12009 case EL_DYNABOMB_PLAYER_2_ACTIVE:
12010 case EL_DYNABOMB_PLAYER_3_ACTIVE:
12011 case EL_DYNABOMB_PLAYER_4_ACTIVE:
12012 case EL_SP_DISK_RED_ACTIVE:
12013 CheckDynamite(x, y);
12016 case EL_AMOEBA_GROWING:
12017 AmoebeWaechst(x, y);
12020 case EL_AMOEBA_SHRINKING:
12021 AmoebaDisappearing(x, y);
12024 #if !USE_NEW_AMOEBA_CODE
12025 case EL_AMOEBA_WET:
12026 case EL_AMOEBA_DRY:
12027 case EL_AMOEBA_FULL:
12029 case EL_EMC_DRIPPER:
12030 AmoebeAbleger(x, y);
12034 case EL_GAME_OF_LIFE:
12039 case EL_EXIT_CLOSED:
12043 case EL_EM_EXIT_CLOSED:
12047 case EL_STEEL_EXIT_CLOSED:
12048 CheckExitSteel(x, y);
12051 case EL_EM_STEEL_EXIT_CLOSED:
12052 CheckExitSteelEM(x, y);
12055 case EL_SP_EXIT_CLOSED:
12059 case EL_EXPANDABLE_WALL_GROWING:
12060 case EL_EXPANDABLE_STEELWALL_GROWING:
12061 MauerWaechst(x, y);
12064 case EL_EXPANDABLE_WALL:
12065 case EL_EXPANDABLE_WALL_HORIZONTAL:
12066 case EL_EXPANDABLE_WALL_VERTICAL:
12067 case EL_EXPANDABLE_WALL_ANY:
12068 case EL_BD_EXPANDABLE_WALL:
12069 MauerAbleger(x, y);
12072 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12073 case EL_EXPANDABLE_STEELWALL_VERTICAL:
12074 case EL_EXPANDABLE_STEELWALL_ANY:
12075 MauerAblegerStahl(x, y);
12079 CheckForDragon(x, y);
12085 case EL_ELEMENT_SNAPPING:
12086 case EL_DIAGONAL_SHRINKING:
12087 case EL_DIAGONAL_GROWING:
12090 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12092 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12097 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12098 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12103 #else // ---------------------------------------------------------------------
12105 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12109 element = Feld[x][y];
12110 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12112 if (IS_ANIMATED(graphic) &&
12113 !IS_MOVING(x, y) &&
12115 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12117 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12118 DrawTwinkleOnField(x, y);
12120 else if ((element == EL_ACID ||
12121 element == EL_EXIT_OPEN ||
12122 element == EL_EM_EXIT_OPEN ||
12123 element == EL_SP_EXIT_OPEN ||
12124 element == EL_STEEL_EXIT_OPEN ||
12125 element == EL_EM_STEEL_EXIT_OPEN ||
12126 element == EL_SP_TERMINAL ||
12127 element == EL_SP_TERMINAL_ACTIVE ||
12128 element == EL_EXTRA_TIME ||
12129 element == EL_SHIELD_NORMAL ||
12130 element == EL_SHIELD_DEADLY) &&
12131 IS_ANIMATED(graphic))
12132 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12133 else if (IS_MOVING(x, y))
12134 ContinueMoving(x, y);
12135 else if (IS_ACTIVE_BOMB(element))
12136 CheckDynamite(x, y);
12137 else if (element == EL_AMOEBA_GROWING)
12138 AmoebeWaechst(x, y);
12139 else if (element == EL_AMOEBA_SHRINKING)
12140 AmoebaDisappearing(x, y);
12142 #if !USE_NEW_AMOEBA_CODE
12143 else if (IS_AMOEBALIVE(element))
12144 AmoebeAbleger(x, y);
12147 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12149 else if (element == EL_EXIT_CLOSED)
12151 else if (element == EL_EM_EXIT_CLOSED)
12153 else if (element == EL_STEEL_EXIT_CLOSED)
12154 CheckExitSteel(x, y);
12155 else if (element == EL_EM_STEEL_EXIT_CLOSED)
12156 CheckExitSteelEM(x, y);
12157 else if (element == EL_SP_EXIT_CLOSED)
12159 else if (element == EL_EXPANDABLE_WALL_GROWING ||
12160 element == EL_EXPANDABLE_STEELWALL_GROWING)
12161 MauerWaechst(x, y);
12162 else if (element == EL_EXPANDABLE_WALL ||
12163 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12164 element == EL_EXPANDABLE_WALL_VERTICAL ||
12165 element == EL_EXPANDABLE_WALL_ANY ||
12166 element == EL_BD_EXPANDABLE_WALL)
12167 MauerAbleger(x, y);
12168 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12169 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12170 element == EL_EXPANDABLE_STEELWALL_ANY)
12171 MauerAblegerStahl(x, y);
12172 else if (element == EL_FLAMES)
12173 CheckForDragon(x, y);
12174 else if (element == EL_EXPLOSION)
12175 ; /* drawing of correct explosion animation is handled separately */
12176 else if (element == EL_ELEMENT_SNAPPING ||
12177 element == EL_DIAGONAL_SHRINKING ||
12178 element == EL_DIAGONAL_GROWING)
12180 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12182 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12184 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12185 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12187 #endif // ---------------------------------------------------------------------
12189 if (IS_BELT_ACTIVE(element))
12190 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12192 if (game.magic_wall_active)
12194 int jx = local_player->jx, jy = local_player->jy;
12196 /* play the element sound at the position nearest to the player */
12197 if ((element == EL_MAGIC_WALL_FULL ||
12198 element == EL_MAGIC_WALL_ACTIVE ||
12199 element == EL_MAGIC_WALL_EMPTYING ||
12200 element == EL_BD_MAGIC_WALL_FULL ||
12201 element == EL_BD_MAGIC_WALL_ACTIVE ||
12202 element == EL_BD_MAGIC_WALL_EMPTYING ||
12203 element == EL_DC_MAGIC_WALL_FULL ||
12204 element == EL_DC_MAGIC_WALL_ACTIVE ||
12205 element == EL_DC_MAGIC_WALL_EMPTYING) &&
12206 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12215 debug_print_timestamp(0, "- time for MAIN loop: -->");
12218 #if USE_NEW_AMOEBA_CODE
12219 /* new experimental amoeba growth stuff */
12220 if (!(FrameCounter % 8))
12222 static unsigned long random = 1684108901;
12224 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12226 x = RND(lev_fieldx);
12227 y = RND(lev_fieldy);
12228 element = Feld[x][y];
12230 if (!IS_PLAYER(x,y) &&
12231 (element == EL_EMPTY ||
12232 CAN_GROW_INTO(element) ||
12233 element == EL_QUICKSAND_EMPTY ||
12234 element == EL_QUICKSAND_FAST_EMPTY ||
12235 element == EL_ACID_SPLASH_LEFT ||
12236 element == EL_ACID_SPLASH_RIGHT))
12238 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12239 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12240 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12241 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12242 Feld[x][y] = EL_AMOEBA_DROP;
12245 random = random * 129 + 1;
12251 if (game.explosions_delayed)
12254 game.explosions_delayed = FALSE;
12256 SCAN_PLAYFIELD(x, y)
12258 element = Feld[x][y];
12260 if (ExplodeField[x][y])
12261 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12262 else if (element == EL_EXPLOSION)
12263 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12265 ExplodeField[x][y] = EX_TYPE_NONE;
12268 game.explosions_delayed = TRUE;
12271 if (game.magic_wall_active)
12273 if (!(game.magic_wall_time_left % 4))
12275 int element = Feld[magic_wall_x][magic_wall_y];
12277 if (element == EL_BD_MAGIC_WALL_FULL ||
12278 element == EL_BD_MAGIC_WALL_ACTIVE ||
12279 element == EL_BD_MAGIC_WALL_EMPTYING)
12280 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12281 else if (element == EL_DC_MAGIC_WALL_FULL ||
12282 element == EL_DC_MAGIC_WALL_ACTIVE ||
12283 element == EL_DC_MAGIC_WALL_EMPTYING)
12284 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12286 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12289 if (game.magic_wall_time_left > 0)
12291 game.magic_wall_time_left--;
12293 if (!game.magic_wall_time_left)
12295 SCAN_PLAYFIELD(x, y)
12297 element = Feld[x][y];
12299 if (element == EL_MAGIC_WALL_ACTIVE ||
12300 element == EL_MAGIC_WALL_FULL)
12302 Feld[x][y] = EL_MAGIC_WALL_DEAD;
12303 DrawLevelField(x, y);
12305 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12306 element == EL_BD_MAGIC_WALL_FULL)
12308 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12309 DrawLevelField(x, y);
12311 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12312 element == EL_DC_MAGIC_WALL_FULL)
12314 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12315 DrawLevelField(x, y);
12319 game.magic_wall_active = FALSE;
12324 if (game.light_time_left > 0)
12326 game.light_time_left--;
12328 if (game.light_time_left == 0)
12329 RedrawAllLightSwitchesAndInvisibleElements();
12332 if (game.timegate_time_left > 0)
12334 game.timegate_time_left--;
12336 if (game.timegate_time_left == 0)
12337 CloseAllOpenTimegates();
12340 if (game.lenses_time_left > 0)
12342 game.lenses_time_left--;
12344 if (game.lenses_time_left == 0)
12345 RedrawAllInvisibleElementsForLenses();
12348 if (game.magnify_time_left > 0)
12350 game.magnify_time_left--;
12352 if (game.magnify_time_left == 0)
12353 RedrawAllInvisibleElementsForMagnifier();
12356 for (i = 0; i < MAX_PLAYERS; i++)
12358 struct PlayerInfo *player = &stored_player[i];
12360 if (SHIELD_ON(player))
12362 if (player->shield_deadly_time_left)
12363 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12364 else if (player->shield_normal_time_left)
12365 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12372 PlayAllPlayersSound();
12374 if (options.debug) /* calculate frames per second */
12376 static unsigned long fps_counter = 0;
12377 static int fps_frames = 0;
12378 unsigned long fps_delay_ms = Counter() - fps_counter;
12382 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
12384 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12387 fps_counter = Counter();
12390 redraw_mask |= REDRAW_FPS;
12393 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12395 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12397 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12399 local_player->show_envelope = 0;
12403 debug_print_timestamp(0, "stop main loop profiling ");
12404 printf("----------------------------------------------------------\n");
12407 /* use random number generator in every frame to make it less predictable */
12408 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12412 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12414 int min_x = x, min_y = y, max_x = x, max_y = y;
12417 for (i = 0; i < MAX_PLAYERS; i++)
12419 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12421 if (!stored_player[i].active || &stored_player[i] == player)
12424 min_x = MIN(min_x, jx);
12425 min_y = MIN(min_y, jy);
12426 max_x = MAX(max_x, jx);
12427 max_y = MAX(max_y, jy);
12430 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12433 static boolean AllPlayersInVisibleScreen()
12437 for (i = 0; i < MAX_PLAYERS; i++)
12439 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12441 if (!stored_player[i].active)
12444 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12451 void ScrollLevel(int dx, int dy)
12454 static Bitmap *bitmap_db_field2 = NULL;
12455 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12462 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
12463 /* only horizontal XOR vertical scroll direction allowed */
12464 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
12469 if (bitmap_db_field2 == NULL)
12470 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
12472 /* needed when blitting directly to same bitmap -- should not be needed with
12473 recent SDL libraries, but apparently does not work in 1.2.11 directly */
12474 BlitBitmap(drawto_field, bitmap_db_field2,
12475 FX + TILEX * (dx == -1) - softscroll_offset,
12476 FY + TILEY * (dy == -1) - softscroll_offset,
12477 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12478 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12479 FX + TILEX * (dx == 1) - softscroll_offset,
12480 FY + TILEY * (dy == 1) - softscroll_offset);
12481 BlitBitmap(bitmap_db_field2, drawto_field,
12482 FX + TILEX * (dx == 1) - softscroll_offset,
12483 FY + TILEY * (dy == 1) - softscroll_offset,
12484 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12485 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12486 FX + TILEX * (dx == 1) - softscroll_offset,
12487 FY + TILEY * (dy == 1) - softscroll_offset);
12492 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
12493 int xsize = (BX2 - BX1 + 1);
12494 int ysize = (BY2 - BY1 + 1);
12495 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
12496 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
12497 int step = (start < end ? +1 : -1);
12499 for (i = start; i != end; i += step)
12501 BlitBitmap(drawto_field, drawto_field,
12502 FX + TILEX * (dx != 0 ? i + step : 0),
12503 FY + TILEY * (dy != 0 ? i + step : 0),
12504 TILEX * (dx != 0 ? 1 : xsize),
12505 TILEY * (dy != 0 ? 1 : ysize),
12506 FX + TILEX * (dx != 0 ? i : 0),
12507 FY + TILEY * (dy != 0 ? i : 0));
12512 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12514 BlitBitmap(drawto_field, drawto_field,
12515 FX + TILEX * (dx == -1) - softscroll_offset,
12516 FY + TILEY * (dy == -1) - softscroll_offset,
12517 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12518 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12519 FX + TILEX * (dx == 1) - softscroll_offset,
12520 FY + TILEY * (dy == 1) - softscroll_offset);
12526 x = (dx == 1 ? BX1 : BX2);
12527 for (y = BY1; y <= BY2; y++)
12528 DrawScreenField(x, y);
12533 y = (dy == 1 ? BY1 : BY2);
12534 for (x = BX1; x <= BX2; x++)
12535 DrawScreenField(x, y);
12538 redraw_mask |= REDRAW_FIELD;
12541 static boolean canFallDown(struct PlayerInfo *player)
12543 int jx = player->jx, jy = player->jy;
12545 return (IN_LEV_FIELD(jx, jy + 1) &&
12546 (IS_FREE(jx, jy + 1) ||
12547 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12548 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12549 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12552 static boolean canPassField(int x, int y, int move_dir)
12554 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12555 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12556 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12557 int nextx = x + dx;
12558 int nexty = y + dy;
12559 int element = Feld[x][y];
12561 return (IS_PASSABLE_FROM(element, opposite_dir) &&
12562 !CAN_MOVE(element) &&
12563 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12564 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12565 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12568 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12570 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12571 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12572 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12576 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12577 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12578 (IS_DIGGABLE(Feld[newx][newy]) ||
12579 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12580 canPassField(newx, newy, move_dir)));
12583 static void CheckGravityMovement(struct PlayerInfo *player)
12585 #if USE_PLAYER_GRAVITY
12586 if (player->gravity && !player->programmed_action)
12588 if (game.gravity && !player->programmed_action)
12591 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12592 int move_dir_vertical = player->effective_action & MV_VERTICAL;
12593 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12594 int jx = player->jx, jy = player->jy;
12595 boolean player_is_moving_to_valid_field =
12596 (!player_is_snapping &&
12597 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12598 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12599 boolean player_can_fall_down = canFallDown(player);
12601 if (player_can_fall_down &&
12602 !player_is_moving_to_valid_field)
12603 player->programmed_action = MV_DOWN;
12607 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12609 return CheckGravityMovement(player);
12611 #if USE_PLAYER_GRAVITY
12612 if (player->gravity && !player->programmed_action)
12614 if (game.gravity && !player->programmed_action)
12617 int jx = player->jx, jy = player->jy;
12618 boolean field_under_player_is_free =
12619 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12620 boolean player_is_standing_on_valid_field =
12621 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12622 (IS_WALKABLE(Feld[jx][jy]) &&
12623 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12625 if (field_under_player_is_free && !player_is_standing_on_valid_field)
12626 player->programmed_action = MV_DOWN;
12631 MovePlayerOneStep()
12632 -----------------------------------------------------------------------------
12633 dx, dy: direction (non-diagonal) to try to move the player to
12634 real_dx, real_dy: direction as read from input device (can be diagonal)
12637 boolean MovePlayerOneStep(struct PlayerInfo *player,
12638 int dx, int dy, int real_dx, int real_dy)
12640 int jx = player->jx, jy = player->jy;
12641 int new_jx = jx + dx, new_jy = jy + dy;
12642 #if !USE_FIXED_DONT_RUN_INTO
12646 boolean player_can_move = !player->cannot_move;
12648 if (!player->active || (!dx && !dy))
12649 return MP_NO_ACTION;
12651 player->MovDir = (dx < 0 ? MV_LEFT :
12652 dx > 0 ? MV_RIGHT :
12654 dy > 0 ? MV_DOWN : MV_NONE);
12656 if (!IN_LEV_FIELD(new_jx, new_jy))
12657 return MP_NO_ACTION;
12659 if (!player_can_move)
12661 if (player->MovPos == 0)
12663 player->is_moving = FALSE;
12664 player->is_digging = FALSE;
12665 player->is_collecting = FALSE;
12666 player->is_snapping = FALSE;
12667 player->is_pushing = FALSE;
12672 if (!options.network && game.centered_player_nr == -1 &&
12673 !AllPlayersInSight(player, new_jx, new_jy))
12674 return MP_NO_ACTION;
12676 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
12677 return MP_NO_ACTION;
12680 #if !USE_FIXED_DONT_RUN_INTO
12681 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
12683 /* (moved to DigField()) */
12684 if (player_can_move && DONT_RUN_INTO(element))
12686 if (element == EL_ACID && dx == 0 && dy == 1)
12688 SplashAcid(new_jx, new_jy);
12689 Feld[jx][jy] = EL_PLAYER_1;
12690 InitMovingField(jx, jy, MV_DOWN);
12691 Store[jx][jy] = EL_ACID;
12692 ContinueMoving(jx, jy);
12693 BuryPlayer(player);
12696 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12702 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12703 if (can_move != MP_MOVING)
12706 /* check if DigField() has caused relocation of the player */
12707 if (player->jx != jx || player->jy != jy)
12708 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12710 StorePlayer[jx][jy] = 0;
12711 player->last_jx = jx;
12712 player->last_jy = jy;
12713 player->jx = new_jx;
12714 player->jy = new_jy;
12715 StorePlayer[new_jx][new_jy] = player->element_nr;
12717 if (player->move_delay_value_next != -1)
12719 player->move_delay_value = player->move_delay_value_next;
12720 player->move_delay_value_next = -1;
12724 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12726 player->step_counter++;
12728 PlayerVisit[jx][jy] = FrameCounter;
12730 #if USE_UFAST_PLAYER_EXIT_BUGFIX
12731 player->is_moving = TRUE;
12735 /* should better be called in MovePlayer(), but this breaks some tapes */
12736 ScrollPlayer(player, SCROLL_INIT);
12742 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12744 int jx = player->jx, jy = player->jy;
12745 int old_jx = jx, old_jy = jy;
12746 int moved = MP_NO_ACTION;
12748 if (!player->active)
12753 if (player->MovPos == 0)
12755 player->is_moving = FALSE;
12756 player->is_digging = FALSE;
12757 player->is_collecting = FALSE;
12758 player->is_snapping = FALSE;
12759 player->is_pushing = FALSE;
12765 if (player->move_delay > 0)
12768 player->move_delay = -1; /* set to "uninitialized" value */
12770 /* store if player is automatically moved to next field */
12771 player->is_auto_moving = (player->programmed_action != MV_NONE);
12773 /* remove the last programmed player action */
12774 player->programmed_action = 0;
12776 if (player->MovPos)
12778 /* should only happen if pre-1.2 tape recordings are played */
12779 /* this is only for backward compatibility */
12781 int original_move_delay_value = player->move_delay_value;
12784 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
12788 /* scroll remaining steps with finest movement resolution */
12789 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12791 while (player->MovPos)
12793 ScrollPlayer(player, SCROLL_GO_ON);
12794 ScrollScreen(NULL, SCROLL_GO_ON);
12796 AdvanceFrameAndPlayerCounters(player->index_nr);
12802 player->move_delay_value = original_move_delay_value;
12805 player->is_active = FALSE;
12807 if (player->last_move_dir & MV_HORIZONTAL)
12809 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12810 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12814 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12815 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12818 #if USE_FIXED_BORDER_RUNNING_GFX
12819 if (!moved && !player->is_active)
12821 player->is_moving = FALSE;
12822 player->is_digging = FALSE;
12823 player->is_collecting = FALSE;
12824 player->is_snapping = FALSE;
12825 player->is_pushing = FALSE;
12833 if (moved & MP_MOVING && !ScreenMovPos &&
12834 (player->index_nr == game.centered_player_nr ||
12835 game.centered_player_nr == -1))
12837 if (moved & MP_MOVING && !ScreenMovPos &&
12838 (player == local_player || !options.network))
12841 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12842 int offset = game.scroll_delay_value;
12844 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12846 /* actual player has left the screen -- scroll in that direction */
12847 if (jx != old_jx) /* player has moved horizontally */
12848 scroll_x += (jx - old_jx);
12849 else /* player has moved vertically */
12850 scroll_y += (jy - old_jy);
12854 if (jx != old_jx) /* player has moved horizontally */
12856 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
12857 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12858 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12860 /* don't scroll over playfield boundaries */
12861 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12862 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12864 /* don't scroll more than one field at a time */
12865 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12867 /* don't scroll against the player's moving direction */
12868 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
12869 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12870 scroll_x = old_scroll_x;
12872 else /* player has moved vertically */
12874 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
12875 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12876 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12878 /* don't scroll over playfield boundaries */
12879 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12880 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12882 /* don't scroll more than one field at a time */
12883 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12885 /* don't scroll against the player's moving direction */
12886 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
12887 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12888 scroll_y = old_scroll_y;
12892 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12895 if (!options.network && game.centered_player_nr == -1 &&
12896 !AllPlayersInVisibleScreen())
12898 scroll_x = old_scroll_x;
12899 scroll_y = old_scroll_y;
12903 if (!options.network && !AllPlayersInVisibleScreen())
12905 scroll_x = old_scroll_x;
12906 scroll_y = old_scroll_y;
12911 ScrollScreen(player, SCROLL_INIT);
12912 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12917 player->StepFrame = 0;
12919 if (moved & MP_MOVING)
12921 if (old_jx != jx && old_jy == jy)
12922 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12923 else if (old_jx == jx && old_jy != jy)
12924 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12926 DrawLevelField(jx, jy); /* for "crumbled sand" */
12928 player->last_move_dir = player->MovDir;
12929 player->is_moving = TRUE;
12930 player->is_snapping = FALSE;
12931 player->is_switching = FALSE;
12932 player->is_dropping = FALSE;
12933 player->is_dropping_pressed = FALSE;
12934 player->drop_pressed_delay = 0;
12937 /* should better be called here than above, but this breaks some tapes */
12938 ScrollPlayer(player, SCROLL_INIT);
12943 CheckGravityMovementWhenNotMoving(player);
12945 player->is_moving = FALSE;
12947 /* at this point, the player is allowed to move, but cannot move right now
12948 (e.g. because of something blocking the way) -- ensure that the player
12949 is also allowed to move in the next frame (in old versions before 3.1.1,
12950 the player was forced to wait again for eight frames before next try) */
12952 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12953 player->move_delay = 0; /* allow direct movement in the next frame */
12956 if (player->move_delay == -1) /* not yet initialized by DigField() */
12957 player->move_delay = player->move_delay_value;
12959 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12961 TestIfPlayerTouchesBadThing(jx, jy);
12962 TestIfPlayerTouchesCustomElement(jx, jy);
12965 if (!player->active)
12966 RemovePlayer(player);
12971 void ScrollPlayer(struct PlayerInfo *player, int mode)
12973 int jx = player->jx, jy = player->jy;
12974 int last_jx = player->last_jx, last_jy = player->last_jy;
12975 int move_stepsize = TILEX / player->move_delay_value;
12977 #if USE_NEW_PLAYER_SPEED
12978 if (!player->active)
12981 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
12984 if (!player->active || player->MovPos == 0)
12988 if (mode == SCROLL_INIT)
12990 player->actual_frame_counter = FrameCounter;
12991 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12993 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12994 Feld[last_jx][last_jy] == EL_EMPTY)
12996 int last_field_block_delay = 0; /* start with no blocking at all */
12997 int block_delay_adjustment = player->block_delay_adjustment;
12999 /* if player blocks last field, add delay for exactly one move */
13000 if (player->block_last_field)
13002 last_field_block_delay += player->move_delay_value;
13004 /* when blocking enabled, prevent moving up despite gravity */
13005 #if USE_PLAYER_GRAVITY
13006 if (player->gravity && player->MovDir == MV_UP)
13007 block_delay_adjustment = -1;
13009 if (game.gravity && player->MovDir == MV_UP)
13010 block_delay_adjustment = -1;
13014 /* add block delay adjustment (also possible when not blocking) */
13015 last_field_block_delay += block_delay_adjustment;
13017 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13018 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13021 #if USE_NEW_PLAYER_SPEED
13022 if (player->MovPos != 0) /* player has not yet reached destination */
13028 else if (!FrameReached(&player->actual_frame_counter, 1))
13031 #if USE_NEW_PLAYER_SPEED
13032 if (player->MovPos != 0)
13034 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13035 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13037 /* before DrawPlayer() to draw correct player graphic for this case */
13038 if (player->MovPos == 0)
13039 CheckGravityMovement(player);
13042 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13043 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13045 /* before DrawPlayer() to draw correct player graphic for this case */
13046 if (player->MovPos == 0)
13047 CheckGravityMovement(player);
13050 if (player->MovPos == 0) /* player reached destination field */
13052 if (player->move_delay_reset_counter > 0)
13054 player->move_delay_reset_counter--;
13056 if (player->move_delay_reset_counter == 0)
13058 /* continue with normal speed after quickly moving through gate */
13059 HALVE_PLAYER_SPEED(player);
13061 /* be able to make the next move without delay */
13062 player->move_delay = 0;
13066 player->last_jx = jx;
13067 player->last_jy = jy;
13069 if (Feld[jx][jy] == EL_EXIT_OPEN ||
13070 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13071 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13072 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13073 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13074 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
13076 DrawPlayer(player); /* needed here only to cleanup last field */
13077 RemovePlayer(player);
13079 if (local_player->friends_still_needed == 0 ||
13080 IS_SP_ELEMENT(Feld[jx][jy]))
13081 PlayerWins(player);
13084 /* this breaks one level: "machine", level 000 */
13086 int move_direction = player->MovDir;
13087 int enter_side = MV_DIR_OPPOSITE(move_direction);
13088 int leave_side = move_direction;
13089 int old_jx = last_jx;
13090 int old_jy = last_jy;
13091 int old_element = Feld[old_jx][old_jy];
13092 int new_element = Feld[jx][jy];
13094 if (IS_CUSTOM_ELEMENT(old_element))
13095 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13097 player->index_bit, leave_side);
13099 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13100 CE_PLAYER_LEAVES_X,
13101 player->index_bit, leave_side);
13103 if (IS_CUSTOM_ELEMENT(new_element))
13104 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13105 player->index_bit, enter_side);
13107 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13108 CE_PLAYER_ENTERS_X,
13109 player->index_bit, enter_side);
13111 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13112 CE_MOVE_OF_X, move_direction);
13115 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13117 TestIfPlayerTouchesBadThing(jx, jy);
13118 TestIfPlayerTouchesCustomElement(jx, jy);
13120 /* needed because pushed element has not yet reached its destination,
13121 so it would trigger a change event at its previous field location */
13122 if (!player->is_pushing)
13123 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
13125 if (!player->active)
13126 RemovePlayer(player);
13129 if (!local_player->LevelSolved && level.use_step_counter)
13139 if (TimeLeft <= 10 && setup.time_limit)
13140 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13143 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13145 DisplayGameControlValues();
13147 DrawGameValue_Time(TimeLeft);
13150 if (!TimeLeft && setup.time_limit)
13151 for (i = 0; i < MAX_PLAYERS; i++)
13152 KillPlayer(&stored_player[i]);
13155 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13157 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13159 DisplayGameControlValues();
13162 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13163 DrawGameValue_Time(TimePlayed);
13167 if (tape.single_step && tape.recording && !tape.pausing &&
13168 !player->programmed_action)
13169 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13173 void ScrollScreen(struct PlayerInfo *player, int mode)
13175 static unsigned long screen_frame_counter = 0;
13177 if (mode == SCROLL_INIT)
13179 /* set scrolling step size according to actual player's moving speed */
13180 ScrollStepSize = TILEX / player->move_delay_value;
13182 screen_frame_counter = FrameCounter;
13183 ScreenMovDir = player->MovDir;
13184 ScreenMovPos = player->MovPos;
13185 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13188 else if (!FrameReached(&screen_frame_counter, 1))
13193 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13194 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13195 redraw_mask |= REDRAW_FIELD;
13198 ScreenMovDir = MV_NONE;
13201 void TestIfPlayerTouchesCustomElement(int x, int y)
13203 static int xy[4][2] =
13210 static int trigger_sides[4][2] =
13212 /* center side border side */
13213 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13214 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13215 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13216 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13218 static int touch_dir[4] =
13220 MV_LEFT | MV_RIGHT,
13225 int center_element = Feld[x][y]; /* should always be non-moving! */
13228 for (i = 0; i < NUM_DIRECTIONS; i++)
13230 int xx = x + xy[i][0];
13231 int yy = y + xy[i][1];
13232 int center_side = trigger_sides[i][0];
13233 int border_side = trigger_sides[i][1];
13234 int border_element;
13236 if (!IN_LEV_FIELD(xx, yy))
13239 if (IS_PLAYER(x, y))
13241 struct PlayerInfo *player = PLAYERINFO(x, y);
13243 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13244 border_element = Feld[xx][yy]; /* may be moving! */
13245 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13246 border_element = Feld[xx][yy];
13247 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13248 border_element = MovingOrBlocked2Element(xx, yy);
13250 continue; /* center and border element do not touch */
13252 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13253 player->index_bit, border_side);
13254 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13255 CE_PLAYER_TOUCHES_X,
13256 player->index_bit, border_side);
13258 else if (IS_PLAYER(xx, yy))
13260 struct PlayerInfo *player = PLAYERINFO(xx, yy);
13262 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13264 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13265 continue; /* center and border element do not touch */
13268 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13269 player->index_bit, center_side);
13270 CheckTriggeredElementChangeByPlayer(x, y, center_element,
13271 CE_PLAYER_TOUCHES_X,
13272 player->index_bit, center_side);
13278 #if USE_ELEMENT_TOUCHING_BUGFIX
13280 void TestIfElementTouchesCustomElement(int x, int y)
13282 static int xy[4][2] =
13289 static int trigger_sides[4][2] =
13291 /* center side border side */
13292 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13293 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13294 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13295 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13297 static int touch_dir[4] =
13299 MV_LEFT | MV_RIGHT,
13304 boolean change_center_element = FALSE;
13305 int center_element = Feld[x][y]; /* should always be non-moving! */
13306 int border_element_old[NUM_DIRECTIONS];
13309 for (i = 0; i < NUM_DIRECTIONS; i++)
13311 int xx = x + xy[i][0];
13312 int yy = y + xy[i][1];
13313 int border_element;
13315 border_element_old[i] = -1;
13317 if (!IN_LEV_FIELD(xx, yy))
13320 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13321 border_element = Feld[xx][yy]; /* may be moving! */
13322 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13323 border_element = Feld[xx][yy];
13324 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13325 border_element = MovingOrBlocked2Element(xx, yy);
13327 continue; /* center and border element do not touch */
13329 border_element_old[i] = border_element;
13332 for (i = 0; i < NUM_DIRECTIONS; i++)
13334 int xx = x + xy[i][0];
13335 int yy = y + xy[i][1];
13336 int center_side = trigger_sides[i][0];
13337 int border_element = border_element_old[i];
13339 if (border_element == -1)
13342 /* check for change of border element */
13343 CheckElementChangeBySide(xx, yy, border_element, center_element,
13344 CE_TOUCHING_X, center_side);
13347 for (i = 0; i < NUM_DIRECTIONS; i++)
13349 int border_side = trigger_sides[i][1];
13350 int border_element = border_element_old[i];
13352 if (border_element == -1)
13355 /* check for change of center element (but change it only once) */
13356 if (!change_center_element)
13357 change_center_element =
13358 CheckElementChangeBySide(x, y, center_element, border_element,
13359 CE_TOUCHING_X, border_side);
13365 void TestIfElementTouchesCustomElement_OLD(int x, int y)
13367 static int xy[4][2] =
13374 static int trigger_sides[4][2] =
13376 /* center side border side */
13377 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13378 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13379 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13380 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13382 static int touch_dir[4] =
13384 MV_LEFT | MV_RIGHT,
13389 boolean change_center_element = FALSE;
13390 int center_element = Feld[x][y]; /* should always be non-moving! */
13393 for (i = 0; i < NUM_DIRECTIONS; i++)
13395 int xx = x + xy[i][0];
13396 int yy = y + xy[i][1];
13397 int center_side = trigger_sides[i][0];
13398 int border_side = trigger_sides[i][1];
13399 int border_element;
13401 if (!IN_LEV_FIELD(xx, yy))
13404 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13405 border_element = Feld[xx][yy]; /* may be moving! */
13406 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13407 border_element = Feld[xx][yy];
13408 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13409 border_element = MovingOrBlocked2Element(xx, yy);
13411 continue; /* center and border element do not touch */
13413 /* check for change of center element (but change it only once) */
13414 if (!change_center_element)
13415 change_center_element =
13416 CheckElementChangeBySide(x, y, center_element, border_element,
13417 CE_TOUCHING_X, border_side);
13419 /* check for change of border element */
13420 CheckElementChangeBySide(xx, yy, border_element, center_element,
13421 CE_TOUCHING_X, center_side);
13427 void TestIfElementHitsCustomElement(int x, int y, int direction)
13429 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13430 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
13431 int hitx = x + dx, hity = y + dy;
13432 int hitting_element = Feld[x][y];
13433 int touched_element;
13435 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13438 touched_element = (IN_LEV_FIELD(hitx, hity) ?
13439 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13441 if (IN_LEV_FIELD(hitx, hity))
13443 int opposite_direction = MV_DIR_OPPOSITE(direction);
13444 int hitting_side = direction;
13445 int touched_side = opposite_direction;
13446 boolean object_hit = (!IS_MOVING(hitx, hity) ||
13447 MovDir[hitx][hity] != direction ||
13448 ABS(MovPos[hitx][hity]) <= TILEY / 2);
13454 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13455 CE_HITTING_X, touched_side);
13457 CheckElementChangeBySide(hitx, hity, touched_element,
13458 hitting_element, CE_HIT_BY_X, hitting_side);
13460 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13461 CE_HIT_BY_SOMETHING, opposite_direction);
13465 /* "hitting something" is also true when hitting the playfield border */
13466 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13467 CE_HITTING_SOMETHING, direction);
13471 void TestIfElementSmashesCustomElement(int x, int y, int direction)
13473 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13474 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
13475 int hitx = x + dx, hity = y + dy;
13476 int hitting_element = Feld[x][y];
13477 int touched_element;
13479 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
13480 !IS_FREE(hitx, hity) &&
13481 (!IS_MOVING(hitx, hity) ||
13482 MovDir[hitx][hity] != direction ||
13483 ABS(MovPos[hitx][hity]) <= TILEY / 2));
13486 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13490 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
13494 touched_element = (IN_LEV_FIELD(hitx, hity) ?
13495 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13497 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13498 EP_CAN_SMASH_EVERYTHING, direction);
13500 if (IN_LEV_FIELD(hitx, hity))
13502 int opposite_direction = MV_DIR_OPPOSITE(direction);
13503 int hitting_side = direction;
13504 int touched_side = opposite_direction;
13506 int touched_element = MovingOrBlocked2Element(hitx, hity);
13509 boolean object_hit = (!IS_MOVING(hitx, hity) ||
13510 MovDir[hitx][hity] != direction ||
13511 ABS(MovPos[hitx][hity]) <= TILEY / 2);
13520 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13521 CE_SMASHED_BY_SOMETHING, opposite_direction);
13523 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13524 CE_OTHER_IS_SMASHING, touched_side);
13526 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13527 CE_OTHER_GETS_SMASHED, hitting_side);
13533 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13535 int i, kill_x = -1, kill_y = -1;
13537 int bad_element = -1;
13538 static int test_xy[4][2] =
13545 static int test_dir[4] =
13553 for (i = 0; i < NUM_DIRECTIONS; i++)
13555 int test_x, test_y, test_move_dir, test_element;
13557 test_x = good_x + test_xy[i][0];
13558 test_y = good_y + test_xy[i][1];
13560 if (!IN_LEV_FIELD(test_x, test_y))
13564 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13566 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13568 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13569 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13571 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13572 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
13576 bad_element = test_element;
13582 if (kill_x != -1 || kill_y != -1)
13584 if (IS_PLAYER(good_x, good_y))
13586 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13588 if (player->shield_deadly_time_left > 0 &&
13589 !IS_INDESTRUCTIBLE(bad_element))
13590 Bang(kill_x, kill_y);
13591 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13592 KillPlayer(player);
13595 Bang(good_x, good_y);
13599 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13601 int i, kill_x = -1, kill_y = -1;
13602 int bad_element = Feld[bad_x][bad_y];
13603 static int test_xy[4][2] =
13610 static int touch_dir[4] =
13612 MV_LEFT | MV_RIGHT,
13617 static int test_dir[4] =
13625 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
13628 for (i = 0; i < NUM_DIRECTIONS; i++)
13630 int test_x, test_y, test_move_dir, test_element;
13632 test_x = bad_x + test_xy[i][0];
13633 test_y = bad_y + test_xy[i][1];
13634 if (!IN_LEV_FIELD(test_x, test_y))
13638 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13640 test_element = Feld[test_x][test_y];
13642 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13643 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13645 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
13646 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
13648 /* good thing is player or penguin that does not move away */
13649 if (IS_PLAYER(test_x, test_y))
13651 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13653 if (bad_element == EL_ROBOT && player->is_moving)
13654 continue; /* robot does not kill player if he is moving */
13656 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13658 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13659 continue; /* center and border element do not touch */
13666 else if (test_element == EL_PENGUIN)
13675 if (kill_x != -1 || kill_y != -1)
13677 if (IS_PLAYER(kill_x, kill_y))
13679 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13681 if (player->shield_deadly_time_left > 0 &&
13682 !IS_INDESTRUCTIBLE(bad_element))
13683 Bang(bad_x, bad_y);
13684 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13685 KillPlayer(player);
13688 Bang(kill_x, kill_y);
13692 void TestIfPlayerTouchesBadThing(int x, int y)
13694 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13697 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13699 TestIfGoodThingHitsBadThing(x, y, move_dir);
13702 void TestIfBadThingTouchesPlayer(int x, int y)
13704 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13707 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13709 TestIfBadThingHitsGoodThing(x, y, move_dir);
13712 void TestIfFriendTouchesBadThing(int x, int y)
13714 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13717 void TestIfBadThingTouchesFriend(int x, int y)
13719 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13722 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13724 int i, kill_x = bad_x, kill_y = bad_y;
13725 static int xy[4][2] =
13733 for (i = 0; i < NUM_DIRECTIONS; i++)
13737 x = bad_x + xy[i][0];
13738 y = bad_y + xy[i][1];
13739 if (!IN_LEV_FIELD(x, y))
13742 element = Feld[x][y];
13743 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13744 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13752 if (kill_x != bad_x || kill_y != bad_y)
13753 Bang(bad_x, bad_y);
13756 void KillPlayer(struct PlayerInfo *player)
13758 int jx = player->jx, jy = player->jy;
13760 if (!player->active)
13763 /* the following code was introduced to prevent an infinite loop when calling
13765 -> CheckTriggeredElementChangeExt()
13766 -> ExecuteCustomElementAction()
13768 -> (infinitely repeating the above sequence of function calls)
13769 which occurs when killing the player while having a CE with the setting
13770 "kill player X when explosion of <player X>"; the solution using a new
13771 field "player->killed" was chosen for backwards compatibility, although
13772 clever use of the fields "player->active" etc. would probably also work */
13774 if (player->killed)
13778 player->killed = TRUE;
13780 /* remove accessible field at the player's position */
13781 Feld[jx][jy] = EL_EMPTY;
13783 /* deactivate shield (else Bang()/Explode() would not work right) */
13784 player->shield_normal_time_left = 0;
13785 player->shield_deadly_time_left = 0;
13788 BuryPlayer(player);
13791 static void KillPlayerUnlessEnemyProtected(int x, int y)
13793 if (!PLAYER_ENEMY_PROTECTED(x, y))
13794 KillPlayer(PLAYERINFO(x, y));
13797 static void KillPlayerUnlessExplosionProtected(int x, int y)
13799 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13800 KillPlayer(PLAYERINFO(x, y));
13803 void BuryPlayer(struct PlayerInfo *player)
13805 int jx = player->jx, jy = player->jy;
13807 if (!player->active)
13810 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13811 PlayLevelSound(jx, jy, SND_GAME_LOSING);
13813 player->GameOver = TRUE;
13814 RemovePlayer(player);
13817 void RemovePlayer(struct PlayerInfo *player)
13819 int jx = player->jx, jy = player->jy;
13820 int i, found = FALSE;
13822 player->present = FALSE;
13823 player->active = FALSE;
13825 if (!ExplodeField[jx][jy])
13826 StorePlayer[jx][jy] = 0;
13828 if (player->is_moving)
13829 DrawLevelField(player->last_jx, player->last_jy);
13831 for (i = 0; i < MAX_PLAYERS; i++)
13832 if (stored_player[i].active)
13836 AllPlayersGone = TRUE;
13842 #if USE_NEW_SNAP_DELAY
13843 static void setFieldForSnapping(int x, int y, int element, int direction)
13845 struct ElementInfo *ei = &element_info[element];
13846 int direction_bit = MV_DIR_TO_BIT(direction);
13847 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13848 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13849 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13851 Feld[x][y] = EL_ELEMENT_SNAPPING;
13852 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13854 ResetGfxAnimation(x, y);
13856 GfxElement[x][y] = element;
13857 GfxAction[x][y] = action;
13858 GfxDir[x][y] = direction;
13859 GfxFrame[x][y] = -1;
13864 =============================================================================
13865 checkDiagonalPushing()
13866 -----------------------------------------------------------------------------
13867 check if diagonal input device direction results in pushing of object
13868 (by checking if the alternative direction is walkable, diggable, ...)
13869 =============================================================================
13872 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13873 int x, int y, int real_dx, int real_dy)
13875 int jx, jy, dx, dy, xx, yy;
13877 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
13880 /* diagonal direction: check alternative direction */
13885 xx = jx + (dx == 0 ? real_dx : 0);
13886 yy = jy + (dy == 0 ? real_dy : 0);
13888 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13892 =============================================================================
13894 -----------------------------------------------------------------------------
13895 x, y: field next to player (non-diagonal) to try to dig to
13896 real_dx, real_dy: direction as read from input device (can be diagonal)
13897 =============================================================================
13900 int DigField(struct PlayerInfo *player,
13901 int oldx, int oldy, int x, int y,
13902 int real_dx, int real_dy, int mode)
13904 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13905 boolean player_was_pushing = player->is_pushing;
13906 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13907 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13908 int jx = oldx, jy = oldy;
13909 int dx = x - jx, dy = y - jy;
13910 int nextx = x + dx, nexty = y + dy;
13911 int move_direction = (dx == -1 ? MV_LEFT :
13912 dx == +1 ? MV_RIGHT :
13914 dy == +1 ? MV_DOWN : MV_NONE);
13915 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13916 int dig_side = MV_DIR_OPPOSITE(move_direction);
13917 int old_element = Feld[jx][jy];
13918 #if USE_FIXED_DONT_RUN_INTO
13919 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13925 if (is_player) /* function can also be called by EL_PENGUIN */
13927 if (player->MovPos == 0)
13929 player->is_digging = FALSE;
13930 player->is_collecting = FALSE;
13933 if (player->MovPos == 0) /* last pushing move finished */
13934 player->is_pushing = FALSE;
13936 if (mode == DF_NO_PUSH) /* player just stopped pushing */
13938 player->is_switching = FALSE;
13939 player->push_delay = -1;
13941 return MP_NO_ACTION;
13945 #if !USE_FIXED_DONT_RUN_INTO
13946 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13947 return MP_NO_ACTION;
13950 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13951 old_element = Back[jx][jy];
13953 /* in case of element dropped at player position, check background */
13954 else if (Back[jx][jy] != EL_EMPTY &&
13955 game.engine_version >= VERSION_IDENT(2,2,0,0))
13956 old_element = Back[jx][jy];
13958 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13959 return MP_NO_ACTION; /* field has no opening in this direction */
13961 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13962 return MP_NO_ACTION; /* field has no opening in this direction */
13964 #if USE_FIXED_DONT_RUN_INTO
13965 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13969 Feld[jx][jy] = player->artwork_element;
13970 InitMovingField(jx, jy, MV_DOWN);
13971 Store[jx][jy] = EL_ACID;
13972 ContinueMoving(jx, jy);
13973 BuryPlayer(player);
13975 return MP_DONT_RUN_INTO;
13979 #if USE_FIXED_DONT_RUN_INTO
13980 if (player_can_move && DONT_RUN_INTO(element))
13982 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13984 return MP_DONT_RUN_INTO;
13988 #if USE_FIXED_DONT_RUN_INTO
13989 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13990 return MP_NO_ACTION;
13993 #if !USE_FIXED_DONT_RUN_INTO
13994 element = Feld[x][y];
13997 collect_count = element_info[element].collect_count_initial;
13999 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
14000 return MP_NO_ACTION;
14002 if (game.engine_version < VERSION_IDENT(2,2,0,0))
14003 player_can_move = player_can_move_or_snap;
14005 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14006 game.engine_version >= VERSION_IDENT(2,2,0,0))
14008 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14009 player->index_bit, dig_side);
14010 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14011 player->index_bit, dig_side);
14013 if (element == EL_DC_LANDMINE)
14016 if (Feld[x][y] != element) /* field changed by snapping */
14019 return MP_NO_ACTION;
14022 #if USE_PLAYER_GRAVITY
14023 if (player->gravity && is_player && !player->is_auto_moving &&
14024 canFallDown(player) && move_direction != MV_DOWN &&
14025 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14026 return MP_NO_ACTION; /* player cannot walk here due to gravity */
14028 if (game.gravity && is_player && !player->is_auto_moving &&
14029 canFallDown(player) && move_direction != MV_DOWN &&
14030 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14031 return MP_NO_ACTION; /* player cannot walk here due to gravity */
14034 if (player_can_move &&
14035 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14037 int sound_element = SND_ELEMENT(element);
14038 int sound_action = ACTION_WALKING;
14040 if (IS_RND_GATE(element))
14042 if (!player->key[RND_GATE_NR(element)])
14043 return MP_NO_ACTION;
14045 else if (IS_RND_GATE_GRAY(element))
14047 if (!player->key[RND_GATE_GRAY_NR(element)])
14048 return MP_NO_ACTION;
14050 else if (IS_RND_GATE_GRAY_ACTIVE(element))
14052 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14053 return MP_NO_ACTION;
14055 else if (element == EL_EXIT_OPEN ||
14056 element == EL_EM_EXIT_OPEN ||
14057 element == EL_STEEL_EXIT_OPEN ||
14058 element == EL_EM_STEEL_EXIT_OPEN ||
14059 element == EL_SP_EXIT_OPEN ||
14060 element == EL_SP_EXIT_OPENING)
14062 sound_action = ACTION_PASSING; /* player is passing exit */
14064 else if (element == EL_EMPTY)
14066 sound_action = ACTION_MOVING; /* nothing to walk on */
14069 /* play sound from background or player, whatever is available */
14070 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14071 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14073 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14075 else if (player_can_move &&
14076 IS_PASSABLE(element) && canPassField(x, y, move_direction))
14078 if (!ACCESS_FROM(element, opposite_direction))
14079 return MP_NO_ACTION; /* field not accessible from this direction */
14081 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
14082 return MP_NO_ACTION;
14084 if (IS_EM_GATE(element))
14086 if (!player->key[EM_GATE_NR(element)])
14087 return MP_NO_ACTION;
14089 else if (IS_EM_GATE_GRAY(element))
14091 if (!player->key[EM_GATE_GRAY_NR(element)])
14092 return MP_NO_ACTION;
14094 else if (IS_EM_GATE_GRAY_ACTIVE(element))
14096 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14097 return MP_NO_ACTION;
14099 else if (IS_EMC_GATE(element))
14101 if (!player->key[EMC_GATE_NR(element)])
14102 return MP_NO_ACTION;
14104 else if (IS_EMC_GATE_GRAY(element))
14106 if (!player->key[EMC_GATE_GRAY_NR(element)])
14107 return MP_NO_ACTION;
14109 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14111 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14112 return MP_NO_ACTION;
14114 else if (element == EL_DC_GATE_WHITE ||
14115 element == EL_DC_GATE_WHITE_GRAY ||
14116 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14118 if (player->num_white_keys == 0)
14119 return MP_NO_ACTION;
14121 player->num_white_keys--;
14123 else if (IS_SP_PORT(element))
14125 if (element == EL_SP_GRAVITY_PORT_LEFT ||
14126 element == EL_SP_GRAVITY_PORT_RIGHT ||
14127 element == EL_SP_GRAVITY_PORT_UP ||
14128 element == EL_SP_GRAVITY_PORT_DOWN)
14129 #if USE_PLAYER_GRAVITY
14130 player->gravity = !player->gravity;
14132 game.gravity = !game.gravity;
14134 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14135 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14136 element == EL_SP_GRAVITY_ON_PORT_UP ||
14137 element == EL_SP_GRAVITY_ON_PORT_DOWN)
14138 #if USE_PLAYER_GRAVITY
14139 player->gravity = TRUE;
14141 game.gravity = TRUE;
14143 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14144 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14145 element == EL_SP_GRAVITY_OFF_PORT_UP ||
14146 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14147 #if USE_PLAYER_GRAVITY
14148 player->gravity = FALSE;
14150 game.gravity = FALSE;
14154 /* automatically move to the next field with double speed */
14155 player->programmed_action = move_direction;
14157 if (player->move_delay_reset_counter == 0)
14159 player->move_delay_reset_counter = 2; /* two double speed steps */
14161 DOUBLE_PLAYER_SPEED(player);
14164 PlayLevelSoundAction(x, y, ACTION_PASSING);
14166 else if (player_can_move_or_snap && IS_DIGGABLE(element))
14170 if (mode != DF_SNAP)
14172 GfxElement[x][y] = GFX_ELEMENT(element);
14173 player->is_digging = TRUE;
14176 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14178 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14179 player->index_bit, dig_side);
14181 if (mode == DF_SNAP)
14183 #if USE_NEW_SNAP_DELAY
14184 if (level.block_snap_field)
14185 setFieldForSnapping(x, y, element, move_direction);
14187 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14189 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14192 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14193 player->index_bit, dig_side);
14196 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14200 if (is_player && mode != DF_SNAP)
14202 GfxElement[x][y] = element;
14203 player->is_collecting = TRUE;
14206 if (element == EL_SPEED_PILL)
14208 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14210 else if (element == EL_EXTRA_TIME && level.time > 0)
14212 TimeLeft += level.extra_time;
14215 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14217 DisplayGameControlValues();
14219 DrawGameValue_Time(TimeLeft);
14222 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14224 player->shield_normal_time_left += level.shield_normal_time;
14225 if (element == EL_SHIELD_DEADLY)
14226 player->shield_deadly_time_left += level.shield_deadly_time;
14228 else if (element == EL_DYNAMITE ||
14229 element == EL_EM_DYNAMITE ||
14230 element == EL_SP_DISK_RED)
14232 if (player->inventory_size < MAX_INVENTORY_SIZE)
14233 player->inventory_element[player->inventory_size++] = element;
14235 DrawGameDoorValues();
14237 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14239 player->dynabomb_count++;
14240 player->dynabombs_left++;
14242 else if (element == EL_DYNABOMB_INCREASE_SIZE)
14244 player->dynabomb_size++;
14246 else if (element == EL_DYNABOMB_INCREASE_POWER)
14248 player->dynabomb_xl = TRUE;
14250 else if (IS_KEY(element))
14252 player->key[KEY_NR(element)] = TRUE;
14254 DrawGameDoorValues();
14256 else if (element == EL_DC_KEY_WHITE)
14258 player->num_white_keys++;
14260 /* display white keys? */
14261 /* DrawGameDoorValues(); */
14263 else if (IS_ENVELOPE(element))
14265 player->show_envelope = element;
14267 else if (element == EL_EMC_LENSES)
14269 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14271 RedrawAllInvisibleElementsForLenses();
14273 else if (element == EL_EMC_MAGNIFIER)
14275 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14277 RedrawAllInvisibleElementsForMagnifier();
14279 else if (IS_DROPPABLE(element) ||
14280 IS_THROWABLE(element)) /* can be collected and dropped */
14284 if (collect_count == 0)
14285 player->inventory_infinite_element = element;
14287 for (i = 0; i < collect_count; i++)
14288 if (player->inventory_size < MAX_INVENTORY_SIZE)
14289 player->inventory_element[player->inventory_size++] = element;
14291 DrawGameDoorValues();
14293 else if (collect_count > 0)
14295 local_player->gems_still_needed -= collect_count;
14296 if (local_player->gems_still_needed < 0)
14297 local_player->gems_still_needed = 0;
14300 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
14302 DisplayGameControlValues();
14304 DrawGameValue_Emeralds(local_player->gems_still_needed);
14308 RaiseScoreElement(element);
14309 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14312 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14313 player->index_bit, dig_side);
14315 if (mode == DF_SNAP)
14317 #if USE_NEW_SNAP_DELAY
14318 if (level.block_snap_field)
14319 setFieldForSnapping(x, y, element, move_direction);
14321 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14323 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14326 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14327 player->index_bit, dig_side);
14330 else if (player_can_move_or_snap && IS_PUSHABLE(element))
14332 if (mode == DF_SNAP && element != EL_BD_ROCK)
14333 return MP_NO_ACTION;
14335 if (CAN_FALL(element) && dy)
14336 return MP_NO_ACTION;
14338 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14339 !(element == EL_SPRING && level.use_spring_bug))
14340 return MP_NO_ACTION;
14342 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14343 ((move_direction & MV_VERTICAL &&
14344 ((element_info[element].move_pattern & MV_LEFT &&
14345 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14346 (element_info[element].move_pattern & MV_RIGHT &&
14347 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14348 (move_direction & MV_HORIZONTAL &&
14349 ((element_info[element].move_pattern & MV_UP &&
14350 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14351 (element_info[element].move_pattern & MV_DOWN &&
14352 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14353 return MP_NO_ACTION;
14355 /* do not push elements already moving away faster than player */
14356 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14357 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14358 return MP_NO_ACTION;
14360 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14362 if (player->push_delay_value == -1 || !player_was_pushing)
14363 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14365 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14367 if (player->push_delay_value == -1)
14368 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14370 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14372 if (!player->is_pushing)
14373 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14376 player->is_pushing = TRUE;
14377 player->is_active = TRUE;
14379 if (!(IN_LEV_FIELD(nextx, nexty) &&
14380 (IS_FREE(nextx, nexty) ||
14381 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
14382 IS_SB_ELEMENT(element)))))
14383 return MP_NO_ACTION;
14385 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14386 return MP_NO_ACTION;
14388 if (player->push_delay == -1) /* new pushing; restart delay */
14389 player->push_delay = 0;
14391 if (player->push_delay < player->push_delay_value &&
14392 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14393 element != EL_SPRING && element != EL_BALLOON)
14395 /* make sure that there is no move delay before next try to push */
14396 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14397 player->move_delay = 0;
14399 return MP_NO_ACTION;
14402 if (IS_SB_ELEMENT(element))
14404 if (element == EL_SOKOBAN_FIELD_FULL)
14406 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14407 local_player->sokobanfields_still_needed++;
14410 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14412 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14413 local_player->sokobanfields_still_needed--;
14416 Feld[x][y] = EL_SOKOBAN_OBJECT;
14418 if (Back[x][y] == Back[nextx][nexty])
14419 PlayLevelSoundAction(x, y, ACTION_PUSHING);
14420 else if (Back[x][y] != 0)
14421 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14424 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14427 if (local_player->sokobanfields_still_needed == 0 &&
14428 game.emulation == EMU_SOKOBAN)
14430 PlayerWins(player);
14432 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14436 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14438 InitMovingField(x, y, move_direction);
14439 GfxAction[x][y] = ACTION_PUSHING;
14441 if (mode == DF_SNAP)
14442 ContinueMoving(x, y);
14444 MovPos[x][y] = (dx != 0 ? dx : dy);
14446 Pushed[x][y] = TRUE;
14447 Pushed[nextx][nexty] = TRUE;
14449 if (game.engine_version < VERSION_IDENT(2,2,0,7))
14450 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14452 player->push_delay_value = -1; /* get new value later */
14454 /* check for element change _after_ element has been pushed */
14455 if (game.use_change_when_pushing_bug)
14457 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14458 player->index_bit, dig_side);
14459 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14460 player->index_bit, dig_side);
14463 else if (IS_SWITCHABLE(element))
14465 if (PLAYER_SWITCHING(player, x, y))
14467 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14468 player->index_bit, dig_side);
14473 player->is_switching = TRUE;
14474 player->switch_x = x;
14475 player->switch_y = y;
14477 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14479 if (element == EL_ROBOT_WHEEL)
14481 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14485 game.robot_wheel_active = TRUE;
14487 DrawLevelField(x, y);
14489 else if (element == EL_SP_TERMINAL)
14493 SCAN_PLAYFIELD(xx, yy)
14495 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14497 else if (Feld[xx][yy] == EL_SP_TERMINAL)
14498 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14501 else if (IS_BELT_SWITCH(element))
14503 ToggleBeltSwitch(x, y);
14505 else if (element == EL_SWITCHGATE_SWITCH_UP ||
14506 element == EL_SWITCHGATE_SWITCH_DOWN ||
14507 element == EL_DC_SWITCHGATE_SWITCH_UP ||
14508 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14510 ToggleSwitchgateSwitch(x, y);
14512 else if (element == EL_LIGHT_SWITCH ||
14513 element == EL_LIGHT_SWITCH_ACTIVE)
14515 ToggleLightSwitch(x, y);
14517 else if (element == EL_TIMEGATE_SWITCH ||
14518 element == EL_DC_TIMEGATE_SWITCH)
14520 ActivateTimegateSwitch(x, y);
14522 else if (element == EL_BALLOON_SWITCH_LEFT ||
14523 element == EL_BALLOON_SWITCH_RIGHT ||
14524 element == EL_BALLOON_SWITCH_UP ||
14525 element == EL_BALLOON_SWITCH_DOWN ||
14526 element == EL_BALLOON_SWITCH_NONE ||
14527 element == EL_BALLOON_SWITCH_ANY)
14529 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
14530 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14531 element == EL_BALLOON_SWITCH_UP ? MV_UP :
14532 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
14533 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
14536 else if (element == EL_LAMP)
14538 Feld[x][y] = EL_LAMP_ACTIVE;
14539 local_player->lights_still_needed--;
14541 ResetGfxAnimation(x, y);
14542 DrawLevelField(x, y);
14544 else if (element == EL_TIME_ORB_FULL)
14546 Feld[x][y] = EL_TIME_ORB_EMPTY;
14548 if (level.time > 0 || level.use_time_orb_bug)
14550 TimeLeft += level.time_orb_time;
14553 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14555 DisplayGameControlValues();
14557 DrawGameValue_Time(TimeLeft);
14561 ResetGfxAnimation(x, y);
14562 DrawLevelField(x, y);
14564 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14565 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14569 game.ball_state = !game.ball_state;
14571 SCAN_PLAYFIELD(xx, yy)
14573 int e = Feld[xx][yy];
14575 if (game.ball_state)
14577 if (e == EL_EMC_MAGIC_BALL)
14578 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14579 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14580 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14584 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14585 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14586 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14587 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14592 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14593 player->index_bit, dig_side);
14595 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14596 player->index_bit, dig_side);
14598 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14599 player->index_bit, dig_side);
14605 if (!PLAYER_SWITCHING(player, x, y))
14607 player->is_switching = TRUE;
14608 player->switch_x = x;
14609 player->switch_y = y;
14611 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14612 player->index_bit, dig_side);
14613 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14614 player->index_bit, dig_side);
14616 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14617 player->index_bit, dig_side);
14618 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14619 player->index_bit, dig_side);
14622 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14623 player->index_bit, dig_side);
14624 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14625 player->index_bit, dig_side);
14627 return MP_NO_ACTION;
14630 player->push_delay = -1;
14632 if (is_player) /* function can also be called by EL_PENGUIN */
14634 if (Feld[x][y] != element) /* really digged/collected something */
14636 player->is_collecting = !player->is_digging;
14637 player->is_active = TRUE;
14644 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14646 int jx = player->jx, jy = player->jy;
14647 int x = jx + dx, y = jy + dy;
14648 int snap_direction = (dx == -1 ? MV_LEFT :
14649 dx == +1 ? MV_RIGHT :
14651 dy == +1 ? MV_DOWN : MV_NONE);
14652 boolean can_continue_snapping = (level.continuous_snapping &&
14653 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14655 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14658 if (!player->active || !IN_LEV_FIELD(x, y))
14666 if (player->MovPos == 0)
14667 player->is_pushing = FALSE;
14669 player->is_snapping = FALSE;
14671 if (player->MovPos == 0)
14673 player->is_moving = FALSE;
14674 player->is_digging = FALSE;
14675 player->is_collecting = FALSE;
14681 #if USE_NEW_CONTINUOUS_SNAPPING
14682 /* prevent snapping with already pressed snap key when not allowed */
14683 if (player->is_snapping && !can_continue_snapping)
14686 if (player->is_snapping)
14690 player->MovDir = snap_direction;
14692 if (player->MovPos == 0)
14694 player->is_moving = FALSE;
14695 player->is_digging = FALSE;
14696 player->is_collecting = FALSE;
14699 player->is_dropping = FALSE;
14700 player->is_dropping_pressed = FALSE;
14701 player->drop_pressed_delay = 0;
14703 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14706 player->is_snapping = TRUE;
14707 player->is_active = TRUE;
14709 if (player->MovPos == 0)
14711 player->is_moving = FALSE;
14712 player->is_digging = FALSE;
14713 player->is_collecting = FALSE;
14716 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
14717 DrawLevelField(player->last_jx, player->last_jy);
14719 DrawLevelField(x, y);
14724 boolean DropElement(struct PlayerInfo *player)
14726 int old_element, new_element;
14727 int dropx = player->jx, dropy = player->jy;
14728 int drop_direction = player->MovDir;
14729 int drop_side = drop_direction;
14731 int drop_element = get_next_dropped_element(player);
14733 int drop_element = (player->inventory_size > 0 ?
14734 player->inventory_element[player->inventory_size - 1] :
14735 player->inventory_infinite_element != EL_UNDEFINED ?
14736 player->inventory_infinite_element :
14737 player->dynabombs_left > 0 ?
14738 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
14742 player->is_dropping_pressed = TRUE;
14744 /* do not drop an element on top of another element; when holding drop key
14745 pressed without moving, dropped element must move away before the next
14746 element can be dropped (this is especially important if the next element
14747 is dynamite, which can be placed on background for historical reasons) */
14748 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14751 if (IS_THROWABLE(drop_element))
14753 dropx += GET_DX_FROM_DIR(drop_direction);
14754 dropy += GET_DY_FROM_DIR(drop_direction);
14756 if (!IN_LEV_FIELD(dropx, dropy))
14760 old_element = Feld[dropx][dropy]; /* old element at dropping position */
14761 new_element = drop_element; /* default: no change when dropping */
14763 /* check if player is active, not moving and ready to drop */
14764 if (!player->active || player->MovPos || player->drop_delay > 0)
14767 /* check if player has anything that can be dropped */
14768 if (new_element == EL_UNDEFINED)
14771 /* check if drop key was pressed long enough for EM style dynamite */
14772 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14775 /* check if anything can be dropped at the current position */
14776 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14779 /* collected custom elements can only be dropped on empty fields */
14780 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14783 if (old_element != EL_EMPTY)
14784 Back[dropx][dropy] = old_element; /* store old element on this field */
14786 ResetGfxAnimation(dropx, dropy);
14787 ResetRandomAnimationValue(dropx, dropy);
14789 if (player->inventory_size > 0 ||
14790 player->inventory_infinite_element != EL_UNDEFINED)
14792 if (player->inventory_size > 0)
14794 player->inventory_size--;
14796 DrawGameDoorValues();
14798 if (new_element == EL_DYNAMITE)
14799 new_element = EL_DYNAMITE_ACTIVE;
14800 else if (new_element == EL_EM_DYNAMITE)
14801 new_element = EL_EM_DYNAMITE_ACTIVE;
14802 else if (new_element == EL_SP_DISK_RED)
14803 new_element = EL_SP_DISK_RED_ACTIVE;
14806 Feld[dropx][dropy] = new_element;
14808 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14809 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14810 el2img(Feld[dropx][dropy]), 0);
14812 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14814 /* needed if previous element just changed to "empty" in the last frame */
14815 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14817 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14818 player->index_bit, drop_side);
14819 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14821 player->index_bit, drop_side);
14823 TestIfElementTouchesCustomElement(dropx, dropy);
14825 else /* player is dropping a dyna bomb */
14827 player->dynabombs_left--;
14829 Feld[dropx][dropy] = new_element;
14831 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14832 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14833 el2img(Feld[dropx][dropy]), 0);
14835 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14838 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14839 InitField_WithBug1(dropx, dropy, FALSE);
14841 new_element = Feld[dropx][dropy]; /* element might have changed */
14843 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14844 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14846 int move_direction, nextx, nexty;
14848 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14849 MovDir[dropx][dropy] = drop_direction;
14851 move_direction = MovDir[dropx][dropy];
14852 nextx = dropx + GET_DX_FROM_DIR(move_direction);
14853 nexty = dropy + GET_DY_FROM_DIR(move_direction);
14855 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14857 #if USE_FIX_IMPACT_COLLISION
14858 /* do not cause impact style collision by dropping elements that can fall */
14859 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14861 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14865 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14866 player->is_dropping = TRUE;
14868 player->drop_pressed_delay = 0;
14869 player->is_dropping_pressed = FALSE;
14871 player->drop_x = dropx;
14872 player->drop_y = dropy;
14877 /* ------------------------------------------------------------------------- */
14878 /* game sound playing functions */
14879 /* ------------------------------------------------------------------------- */
14881 static int *loop_sound_frame = NULL;
14882 static int *loop_sound_volume = NULL;
14884 void InitPlayLevelSound()
14886 int num_sounds = getSoundListSize();
14888 checked_free(loop_sound_frame);
14889 checked_free(loop_sound_volume);
14891 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
14892 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14895 static void PlayLevelSound(int x, int y, int nr)
14897 int sx = SCREENX(x), sy = SCREENY(y);
14898 int volume, stereo_position;
14899 int max_distance = 8;
14900 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14902 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14903 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14906 if (!IN_LEV_FIELD(x, y) ||
14907 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14908 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14911 volume = SOUND_MAX_VOLUME;
14913 if (!IN_SCR_FIELD(sx, sy))
14915 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14916 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14918 volume -= volume * (dx > dy ? dx : dy) / max_distance;
14921 stereo_position = (SOUND_MAX_LEFT +
14922 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14923 (SCR_FIELDX + 2 * max_distance));
14925 if (IS_LOOP_SOUND(nr))
14927 /* This assures that quieter loop sounds do not overwrite louder ones,
14928 while restarting sound volume comparison with each new game frame. */
14930 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14933 loop_sound_volume[nr] = volume;
14934 loop_sound_frame[nr] = FrameCounter;
14937 PlaySoundExt(nr, volume, stereo_position, type);
14940 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14942 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14943 x > LEVELX(BX2) ? LEVELX(BX2) : x,
14944 y < LEVELY(BY1) ? LEVELY(BY1) :
14945 y > LEVELY(BY2) ? LEVELY(BY2) : y,
14949 static void PlayLevelSoundAction(int x, int y, int action)
14951 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14954 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14956 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14958 if (sound_effect != SND_UNDEFINED)
14959 PlayLevelSound(x, y, sound_effect);
14962 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14965 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14967 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14968 PlayLevelSound(x, y, sound_effect);
14971 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14973 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14975 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14976 PlayLevelSound(x, y, sound_effect);
14979 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14981 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14983 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14984 StopSound(sound_effect);
14987 static void PlayLevelMusic()
14989 if (levelset.music[level_nr] != MUS_UNDEFINED)
14990 PlayMusic(levelset.music[level_nr]); /* from config file */
14992 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
14995 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14997 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14998 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14999 int x = xx - 1 - offset;
15000 int y = yy - 1 - offset;
15005 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15009 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15013 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15017 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15021 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15025 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15029 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15032 case SAMPLE_android_clone:
15033 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15036 case SAMPLE_android_move:
15037 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15040 case SAMPLE_spring:
15041 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15045 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15049 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15052 case SAMPLE_eater_eat:
15053 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15057 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15060 case SAMPLE_collect:
15061 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15064 case SAMPLE_diamond:
15065 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15068 case SAMPLE_squash:
15069 /* !!! CHECK THIS !!! */
15071 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15073 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15077 case SAMPLE_wonderfall:
15078 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15082 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15086 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15090 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15094 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15098 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15102 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15105 case SAMPLE_wonder:
15106 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15110 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15113 case SAMPLE_exit_open:
15114 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15117 case SAMPLE_exit_leave:
15118 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15121 case SAMPLE_dynamite:
15122 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15126 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15130 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15134 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15138 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15142 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15146 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15150 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15156 void ChangeTime(int value)
15158 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
15162 /* EMC game engine uses value from time counter of RND game engine */
15163 level.native_em_level->lev->time = *time;
15165 DrawGameValue_Time(*time);
15168 void RaiseScore(int value)
15170 /* EMC game engine and RND game engine have separate score counters */
15171 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
15172 &level.native_em_level->lev->score : &local_player->score);
15176 DrawGameValue_Score(*score);
15180 void RaiseScore(int value)
15182 local_player->score += value;
15185 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
15187 DisplayGameControlValues();
15189 DrawGameValue_Score(local_player->score);
15193 void RaiseScoreElement(int element)
15198 case EL_BD_DIAMOND:
15199 case EL_EMERALD_YELLOW:
15200 case EL_EMERALD_RED:
15201 case EL_EMERALD_PURPLE:
15202 case EL_SP_INFOTRON:
15203 RaiseScore(level.score[SC_EMERALD]);
15206 RaiseScore(level.score[SC_DIAMOND]);
15209 RaiseScore(level.score[SC_CRYSTAL]);
15212 RaiseScore(level.score[SC_PEARL]);
15215 case EL_BD_BUTTERFLY:
15216 case EL_SP_ELECTRON:
15217 RaiseScore(level.score[SC_BUG]);
15220 case EL_BD_FIREFLY:
15221 case EL_SP_SNIKSNAK:
15222 RaiseScore(level.score[SC_SPACESHIP]);
15225 case EL_DARK_YAMYAM:
15226 RaiseScore(level.score[SC_YAMYAM]);
15229 RaiseScore(level.score[SC_ROBOT]);
15232 RaiseScore(level.score[SC_PACMAN]);
15235 RaiseScore(level.score[SC_NUT]);
15238 case EL_EM_DYNAMITE:
15239 case EL_SP_DISK_RED:
15240 case EL_DYNABOMB_INCREASE_NUMBER:
15241 case EL_DYNABOMB_INCREASE_SIZE:
15242 case EL_DYNABOMB_INCREASE_POWER:
15243 RaiseScore(level.score[SC_DYNAMITE]);
15245 case EL_SHIELD_NORMAL:
15246 case EL_SHIELD_DEADLY:
15247 RaiseScore(level.score[SC_SHIELD]);
15249 case EL_EXTRA_TIME:
15250 RaiseScore(level.extra_time_score);
15264 case EL_DC_KEY_WHITE:
15265 RaiseScore(level.score[SC_KEY]);
15268 RaiseScore(element_info[element].collect_score);
15273 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15275 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15277 #if defined(NETWORK_AVALIABLE)
15278 if (options.network)
15279 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15288 FadeSkipNextFadeIn();
15290 fading = fading_none;
15294 OpenDoor(DOOR_CLOSE_1);
15297 game_status = GAME_MODE_MAIN;
15300 DrawAndFadeInMainMenu(REDRAW_FIELD);
15308 FadeOut(REDRAW_FIELD);
15311 game_status = GAME_MODE_MAIN;
15313 DrawAndFadeInMainMenu(REDRAW_FIELD);
15317 else /* continue playing the game */
15319 if (tape.playing && tape.deactivate_display)
15320 TapeDeactivateDisplayOff(TRUE);
15322 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15324 if (tape.playing && tape.deactivate_display)
15325 TapeDeactivateDisplayOn();
15329 void RequestQuitGame(boolean ask_if_really_quit)
15331 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15332 boolean skip_request = AllPlayersGone || quick_quit;
15334 RequestQuitGameExt(skip_request, quick_quit,
15335 "Do you really want to quit the game ?");
15339 /* ------------------------------------------------------------------------- */
15340 /* random generator functions */
15341 /* ------------------------------------------------------------------------- */
15343 unsigned int InitEngineRandom_RND(long seed)
15345 game.num_random_calls = 0;
15348 unsigned int rnd_seed = InitEngineRandom(seed);
15350 printf("::: START RND: %d\n", rnd_seed);
15355 return InitEngineRandom(seed);
15361 unsigned int RND(int max)
15365 game.num_random_calls++;
15367 return GetEngineRandom(max);
15374 /* ------------------------------------------------------------------------- */
15375 /* game engine snapshot handling functions */
15376 /* ------------------------------------------------------------------------- */
15378 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
15380 struct EngineSnapshotInfo
15382 /* runtime values for custom element collect score */
15383 int collect_score[NUM_CUSTOM_ELEMENTS];
15385 /* runtime values for group element choice position */
15386 int choice_pos[NUM_GROUP_ELEMENTS];
15388 /* runtime values for belt position animations */
15389 int belt_graphic[4 * NUM_BELT_PARTS];
15390 int belt_anim_mode[4 * NUM_BELT_PARTS];
15393 struct EngineSnapshotNodeInfo
15400 static struct EngineSnapshotInfo engine_snapshot_rnd;
15401 static ListNode *engine_snapshot_list = NULL;
15402 static char *snapshot_level_identifier = NULL;
15403 static int snapshot_level_nr = -1;
15405 void FreeEngineSnapshot()
15407 while (engine_snapshot_list != NULL)
15408 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
15411 setString(&snapshot_level_identifier, NULL);
15412 snapshot_level_nr = -1;
15415 static void SaveEngineSnapshotValues_RND()
15417 static int belt_base_active_element[4] =
15419 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15420 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15421 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15422 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15426 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15428 int element = EL_CUSTOM_START + i;
15430 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15433 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15435 int element = EL_GROUP_START + i;
15437 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15440 for (i = 0; i < 4; i++)
15442 for (j = 0; j < NUM_BELT_PARTS; j++)
15444 int element = belt_base_active_element[i] + j;
15445 int graphic = el2img(element);
15446 int anim_mode = graphic_info[graphic].anim_mode;
15448 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
15449 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
15454 static void LoadEngineSnapshotValues_RND()
15456 unsigned long num_random_calls = game.num_random_calls;
15459 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15461 int element = EL_CUSTOM_START + i;
15463 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15466 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15468 int element = EL_GROUP_START + i;
15470 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15473 for (i = 0; i < 4; i++)
15475 for (j = 0; j < NUM_BELT_PARTS; j++)
15477 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
15478 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
15480 graphic_info[graphic].anim_mode = anim_mode;
15484 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15486 InitRND(tape.random_seed);
15487 for (i = 0; i < num_random_calls; i++)
15491 if (game.num_random_calls != num_random_calls)
15493 Error(ERR_INFO, "number of random calls out of sync");
15494 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15495 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15496 Error(ERR_EXIT, "this should not happen -- please debug");
15500 static void SaveEngineSnapshotBuffer(void *buffer, int size)
15502 struct EngineSnapshotNodeInfo *bi =
15503 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
15505 bi->buffer_orig = buffer;
15506 bi->buffer_copy = checked_malloc(size);
15509 memcpy(bi->buffer_copy, buffer, size);
15511 addNodeToList(&engine_snapshot_list, NULL, bi);
15514 void SaveEngineSnapshot()
15516 FreeEngineSnapshot(); /* free previous snapshot, if needed */
15518 if (level_editor_test_game) /* do not save snapshots from editor */
15521 /* copy some special values to a structure better suited for the snapshot */
15523 SaveEngineSnapshotValues_RND();
15524 SaveEngineSnapshotValues_EM();
15526 /* save values stored in special snapshot structure */
15528 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15529 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15531 /* save further RND engine values */
15533 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
15534 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
15535 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
15537 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
15538 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
15539 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
15540 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
15542 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15543 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15544 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15545 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15546 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15548 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15549 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15550 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15552 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15554 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15556 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15557 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15559 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
15560 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
15561 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
15562 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15563 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15564 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15565 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15566 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
15567 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
15568 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15569 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
15570 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15571 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15572 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15573 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15574 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15575 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
15576 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
15578 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15579 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15581 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15582 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15583 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15585 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15586 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15588 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15589 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15590 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15591 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15592 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15594 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15595 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15597 /* save level identification information */
15599 setString(&snapshot_level_identifier, leveldir_current->identifier);
15600 snapshot_level_nr = level_nr;
15603 ListNode *node = engine_snapshot_list;
15606 while (node != NULL)
15608 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15613 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15617 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
15619 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
15622 void LoadEngineSnapshot()
15624 ListNode *node = engine_snapshot_list;
15626 if (engine_snapshot_list == NULL)
15629 while (node != NULL)
15631 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
15636 /* restore special values from snapshot structure */
15638 LoadEngineSnapshotValues_RND();
15639 LoadEngineSnapshotValues_EM();
15642 boolean CheckEngineSnapshot()
15644 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15645 snapshot_level_nr == level_nr);
15649 /* ---------- new game button stuff ---------------------------------------- */
15651 /* graphic position values for game buttons */
15652 #define GAME_BUTTON_XSIZE 30
15653 #define GAME_BUTTON_YSIZE 30
15654 #define GAME_BUTTON_XPOS 5
15655 #define GAME_BUTTON_YPOS 215
15656 #define SOUND_BUTTON_XPOS 5
15657 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
15659 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15660 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15661 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15662 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15663 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15664 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15672 } gamebutton_info[NUM_GAME_BUTTONS] =
15676 &game.button.stop.x, &game.button.stop.y,
15677 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
15682 &game.button.pause.x, &game.button.pause.y,
15683 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
15684 GAME_CTRL_ID_PAUSE,
15688 &game.button.play.x, &game.button.play.y,
15689 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
15694 &game.button.sound_music.x, &game.button.sound_music.y,
15695 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
15696 SOUND_CTRL_ID_MUSIC,
15697 "background music on/off"
15700 &game.button.sound_loops.x, &game.button.sound_loops.y,
15701 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
15702 SOUND_CTRL_ID_LOOPS,
15703 "sound loops on/off"
15706 &game.button.sound_simple.x,&game.button.sound_simple.y,
15707 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
15708 SOUND_CTRL_ID_SIMPLE,
15709 "normal sounds on/off"
15713 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
15718 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
15719 GAME_CTRL_ID_PAUSE,
15723 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
15728 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
15729 SOUND_CTRL_ID_MUSIC,
15730 "background music on/off"
15733 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
15734 SOUND_CTRL_ID_LOOPS,
15735 "sound loops on/off"
15738 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
15739 SOUND_CTRL_ID_SIMPLE,
15740 "normal sounds on/off"
15745 void CreateGameButtons()
15749 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15751 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
15752 struct GadgetInfo *gi;
15755 unsigned long event_mask;
15757 int gd_xoffset, gd_yoffset;
15758 int gd_x1, gd_x2, gd_y1, gd_y2;
15761 x = DX + *gamebutton_info[i].x;
15762 y = DY + *gamebutton_info[i].y;
15763 gd_xoffset = gamebutton_info[i].gd_x;
15764 gd_yoffset = gamebutton_info[i].gd_y;
15765 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
15766 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
15768 if (id == GAME_CTRL_ID_STOP ||
15769 id == GAME_CTRL_ID_PAUSE ||
15770 id == GAME_CTRL_ID_PLAY)
15772 button_type = GD_TYPE_NORMAL_BUTTON;
15774 event_mask = GD_EVENT_RELEASED;
15775 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15776 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15780 button_type = GD_TYPE_CHECK_BUTTON;
15782 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15783 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15784 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15785 event_mask = GD_EVENT_PRESSED;
15786 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
15787 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15790 gi = CreateGadget(GDI_CUSTOM_ID, id,
15791 GDI_INFO_TEXT, gamebutton_info[i].infotext,
15796 GDI_X, DX + gd_xoffset,
15797 GDI_Y, DY + gd_yoffset,
15799 GDI_WIDTH, GAME_BUTTON_XSIZE,
15800 GDI_HEIGHT, GAME_BUTTON_YSIZE,
15801 GDI_TYPE, button_type,
15802 GDI_STATE, GD_BUTTON_UNPRESSED,
15803 GDI_CHECKED, checked,
15804 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
15805 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
15806 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
15807 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
15808 GDI_DIRECT_DRAW, FALSE,
15809 GDI_EVENT_MASK, event_mask,
15810 GDI_CALLBACK_ACTION, HandleGameButtons,
15814 Error(ERR_EXIT, "cannot create gadget");
15816 game_gadget[id] = gi;
15820 void FreeGameButtons()
15824 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15825 FreeGadget(game_gadget[i]);
15828 static void MapGameButtons()
15832 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15833 MapGadget(game_gadget[i]);
15836 void UnmapGameButtons()
15840 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15841 UnmapGadget(game_gadget[i]);
15844 void RedrawGameButtons()
15848 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15849 RedrawGadget(game_gadget[i]);
15852 static void HandleGameButtons(struct GadgetInfo *gi)
15854 int id = gi->custom_id;
15856 if (game_status != GAME_MODE_PLAYING)
15861 case GAME_CTRL_ID_STOP:
15865 RequestQuitGame(TRUE);
15868 case GAME_CTRL_ID_PAUSE:
15869 if (options.network)
15871 #if defined(NETWORK_AVALIABLE)
15873 SendToServer_ContinuePlaying();
15875 SendToServer_PausePlaying();
15879 TapeTogglePause(TAPE_TOGGLE_MANUAL);
15882 case GAME_CTRL_ID_PLAY:
15885 #if defined(NETWORK_AVALIABLE)
15886 if (options.network)
15887 SendToServer_ContinuePlaying();
15891 tape.pausing = FALSE;
15892 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
15897 case SOUND_CTRL_ID_MUSIC:
15898 if (setup.sound_music)
15900 setup.sound_music = FALSE;
15903 else if (audio.music_available)
15905 setup.sound = setup.sound_music = TRUE;
15907 SetAudioMode(setup.sound);
15913 case SOUND_CTRL_ID_LOOPS:
15914 if (setup.sound_loops)
15915 setup.sound_loops = FALSE;
15916 else if (audio.loops_available)
15918 setup.sound = setup.sound_loops = TRUE;
15919 SetAudioMode(setup.sound);
15923 case SOUND_CTRL_ID_SIMPLE:
15924 if (setup.sound_simple)
15925 setup.sound_simple = FALSE;
15926 else if (audio.sound_available)
15928 setup.sound = setup.sound_simple = TRUE;
15929 SetAudioMode(setup.sound);