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()
2116 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2117 element_info[i].element_count = 0;
2119 SCAN_PLAYFIELD(x, y)
2121 element_info[Feld[x][y]].element_count++;
2124 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2125 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2126 if (IS_IN_GROUP(j, i))
2127 element_info[EL_GROUP_START + i].element_count +=
2128 element_info[j].element_count;
2131 void UpdateGameControlValues()
2134 int time = (local_player->LevelSolved ?
2135 local_player->LevelSolved_CountingTime :
2136 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2137 level.native_em_level->lev->time :
2138 level.time == 0 ? TimePlayed : TimeLeft);
2139 int score = (local_player->LevelSolved ?
2140 local_player->LevelSolved_CountingScore :
2141 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2142 level.native_em_level->lev->score :
2143 local_player->score);
2144 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2145 level.native_em_level->lev->required :
2146 local_player->gems_still_needed);
2147 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2148 level.native_em_level->lev->required > 0 :
2149 local_player->gems_still_needed > 0 ||
2150 local_player->sokobanfields_still_needed > 0 ||
2151 local_player->lights_still_needed > 0);
2153 UpdatePlayfieldElementCount();
2155 /* update game panel control values */
2157 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2158 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2160 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2161 for (i = 0; i < MAX_NUM_KEYS; i++)
2162 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2163 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2164 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2166 if (game.centered_player_nr == -1)
2168 for (i = 0; i < MAX_PLAYERS; i++)
2170 for (k = 0; k < MAX_NUM_KEYS; k++)
2172 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2174 if (level.native_em_level->ply[i]->keys & (1 << k))
2175 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2176 get_key_element_from_nr(k);
2178 else if (stored_player[i].key[k])
2179 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2180 get_key_element_from_nr(k);
2183 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2184 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2185 level.native_em_level->ply[i]->dynamite;
2187 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2188 stored_player[i].inventory_size;
2190 if (stored_player[i].num_white_keys > 0)
2191 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2194 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2195 stored_player[i].num_white_keys;
2200 int player_nr = game.centered_player_nr;
2202 for (k = 0; k < MAX_NUM_KEYS; k++)
2204 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2206 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2207 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2208 get_key_element_from_nr(k);
2210 else if (stored_player[player_nr].key[k])
2211 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2212 get_key_element_from_nr(k);
2215 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2216 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2217 level.native_em_level->ply[player_nr]->dynamite;
2219 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2220 stored_player[player_nr].inventory_size;
2222 if (stored_player[player_nr].num_white_keys > 0)
2223 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2225 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2226 stored_player[player_nr].num_white_keys;
2229 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2231 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2232 get_inventory_element_from_pos(local_player, i);
2233 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2234 get_inventory_element_from_pos(local_player, -i - 1);
2237 game_panel_controls[GAME_PANEL_SCORE].value = score;
2239 game_panel_controls[GAME_PANEL_TIME].value = time;
2241 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2242 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2243 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2245 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2246 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2248 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2249 local_player->shield_normal_time_left;
2250 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2251 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2253 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2254 local_player->shield_deadly_time_left;
2256 game_panel_controls[GAME_PANEL_EXIT].value =
2257 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2259 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2260 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2261 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2262 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2263 EL_EMC_MAGIC_BALL_SWITCH);
2265 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2266 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2267 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2268 game.light_time_left;
2270 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2271 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2272 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2273 game.timegate_time_left;
2275 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2276 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2278 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2279 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2280 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2281 game.lenses_time_left;
2283 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2284 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2285 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2286 game.magnify_time_left;
2288 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2289 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2290 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2291 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2292 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2293 EL_BALLOON_SWITCH_NONE);
2295 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2296 local_player->dynabomb_count;
2297 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2298 local_player->dynabomb_size;
2299 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2300 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2302 game_panel_controls[GAME_PANEL_PENGUINS].value =
2303 local_player->friends_still_needed;
2305 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2306 local_player->sokobanfields_still_needed;
2307 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2308 local_player->sokobanfields_still_needed;
2310 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2311 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2313 for (i = 0; i < NUM_BELTS; i++)
2315 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2316 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2317 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2318 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2319 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2322 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2323 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2324 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2325 game.magic_wall_time_left;
2327 #if USE_PLAYER_GRAVITY
2328 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2329 local_player->gravity;
2331 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2334 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2335 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2337 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2338 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2339 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2340 game.panel.element[i].id : EL_UNDEFINED);
2342 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2343 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2344 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2345 element_info[game.panel.element_count[i].id].element_count :
2348 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2349 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2350 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2351 element_info[game.panel.ce_score[i].id].collect_score : 0);
2353 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2354 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2355 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2356 element_info[game.panel.ce_score_element[i].id].collect_score :
2359 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2360 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2361 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2363 /* update game panel control frames */
2365 for (i = 0; game_panel_controls[i].nr != -1; i++)
2367 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2369 if (gpc->type == TYPE_ELEMENT)
2371 int last_anim_random_frame = gfx.anim_random_frame;
2372 int element = gpc->value;
2373 int graphic = el2panelimg(element);
2375 if (gpc->value != gpc->last_value)
2378 gpc->gfx_random = INIT_GFX_RANDOM();
2384 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2385 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2386 gpc->gfx_random = INIT_GFX_RANDOM();
2389 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2390 gfx.anim_random_frame = gpc->gfx_random;
2392 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2393 gpc->gfx_frame = element_info[element].collect_score;
2395 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2398 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2399 gfx.anim_random_frame = last_anim_random_frame;
2404 void DisplayGameControlValues()
2406 boolean redraw_panel = FALSE;
2409 for (i = 0; game_panel_controls[i].nr != -1; i++)
2411 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2413 if (PANEL_DEACTIVATED(gpc->pos))
2416 if (gpc->value == gpc->last_value &&
2417 gpc->frame == gpc->last_frame)
2420 redraw_panel = TRUE;
2426 /* copy default game door content to main double buffer */
2427 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2428 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2430 /* redraw game control buttons */
2432 RedrawGameButtons();
2438 game_status = GAME_MODE_PSEUDO_PANEL;
2441 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2443 for (i = 0; game_panel_controls[i].nr != -1; i++)
2447 int nr = game_panel_order[i].nr;
2448 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2450 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2453 struct TextPosInfo *pos = gpc->pos;
2454 int type = gpc->type;
2455 int value = gpc->value;
2456 int frame = gpc->frame;
2458 int last_value = gpc->last_value;
2459 int last_frame = gpc->last_frame;
2461 int size = pos->size;
2462 int font = pos->font;
2463 boolean draw_masked = pos->draw_masked;
2464 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2466 if (PANEL_DEACTIVATED(pos))
2470 if (value == last_value && frame == last_frame)
2474 gpc->last_value = value;
2475 gpc->last_frame = frame;
2478 printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2481 if (type == TYPE_INTEGER)
2483 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2484 nr == GAME_PANEL_TIME)
2486 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2488 if (use_dynamic_size) /* use dynamic number of digits */
2490 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2491 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2492 int size2 = size1 + 1;
2493 int font1 = pos->font;
2494 int font2 = pos->font_alt;
2496 size = (value < value_change ? size1 : size2);
2497 font = (value < value_change ? font1 : font2);
2500 /* clear background if value just changed its size (dynamic digits) */
2501 if ((last_value < value_change) != (value < value_change))
2503 int width1 = size1 * getFontWidth(font1);
2504 int width2 = size2 * getFontWidth(font2);
2505 int max_width = MAX(width1, width2);
2506 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2508 pos->width = max_width;
2510 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2511 max_width, max_height);
2518 /* correct text size if "digits" is zero or less */
2520 size = strlen(int2str(value, size));
2522 /* dynamically correct text alignment */
2523 pos->width = size * getFontWidth(font);
2526 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2527 int2str(value, size), font, mask_mode);
2529 else if (type == TYPE_ELEMENT)
2531 int element, graphic;
2535 int dst_x = PANEL_XPOS(pos);
2536 int dst_y = PANEL_YPOS(pos);
2539 if (value != EL_UNDEFINED && value != EL_EMPTY)
2542 graphic = el2panelimg(value);
2544 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2547 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2551 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2554 width = graphic_info[graphic].width * size / TILESIZE;
2555 height = graphic_info[graphic].height * size / TILESIZE;
2559 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2560 dst_x - src_x, dst_y - src_y);
2561 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2566 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2571 if (value == EL_UNDEFINED || value == EL_EMPTY)
2573 element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2574 graphic = el2panelimg(element);
2576 src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2577 src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2578 src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2583 graphic = el2panelimg(value);
2585 getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2588 width = graphic_info[graphic].width * size / TILESIZE;
2589 height = graphic_info[graphic].height * size / TILESIZE;
2591 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2594 else if (type == TYPE_STRING)
2596 boolean active = (value != 0);
2597 char *state_normal = "off";
2598 char *state_active = "on";
2599 char *state = (active ? state_active : state_normal);
2600 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2601 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2602 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2603 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2605 if (nr == GAME_PANEL_GRAVITY_STATE)
2607 int font1 = pos->font; /* (used for normal state) */
2608 int font2 = pos->font_alt; /* (used for active state) */
2610 int size1 = strlen(state_normal);
2611 int size2 = strlen(state_active);
2612 int width1 = size1 * getFontWidth(font1);
2613 int width2 = size2 * getFontWidth(font2);
2614 int max_width = MAX(width1, width2);
2615 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2617 pos->width = max_width;
2619 /* clear background for values that may have changed its size */
2620 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2621 max_width, max_height);
2624 font = (active ? font2 : font1);
2634 /* don't truncate output if "chars" is zero or less */
2637 /* dynamically correct text alignment */
2638 pos->width = size * getFontWidth(font);
2642 s_cut = getStringCopyN(s, size);
2644 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2645 s_cut, font, mask_mode);
2651 redraw_mask |= REDRAW_DOOR_1;
2654 game_status = GAME_MODE_PLAYING;
2657 void DrawGameValue_Emeralds(int value)
2659 struct TextPosInfo *pos = &game.panel.gems;
2661 int font_nr = pos->font;
2663 int font_nr = FONT_TEXT_2;
2665 int font_width = getFontWidth(font_nr);
2666 int chars = pos->size;
2669 return; /* !!! USE NEW STUFF !!! */
2672 if (PANEL_DEACTIVATED(pos))
2675 pos->width = chars * font_width;
2677 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2680 void DrawGameValue_Dynamite(int value)
2682 struct TextPosInfo *pos = &game.panel.inventory_count;
2684 int font_nr = pos->font;
2686 int font_nr = FONT_TEXT_2;
2688 int font_width = getFontWidth(font_nr);
2689 int chars = pos->size;
2692 return; /* !!! USE NEW STUFF !!! */
2695 if (PANEL_DEACTIVATED(pos))
2698 pos->width = chars * font_width;
2700 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2703 void DrawGameValue_Score(int value)
2705 struct TextPosInfo *pos = &game.panel.score;
2707 int font_nr = pos->font;
2709 int font_nr = FONT_TEXT_2;
2711 int font_width = getFontWidth(font_nr);
2712 int chars = pos->size;
2715 return; /* !!! USE NEW STUFF !!! */
2718 if (PANEL_DEACTIVATED(pos))
2721 pos->width = chars * font_width;
2723 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2726 void DrawGameValue_Time(int value)
2728 struct TextPosInfo *pos = &game.panel.time;
2729 static int last_value = -1;
2732 int chars = pos->size;
2734 int font1_nr = pos->font;
2735 int font2_nr = pos->font_alt;
2737 int font1_nr = FONT_TEXT_2;
2738 int font2_nr = FONT_TEXT_1;
2740 int font_nr = font1_nr;
2741 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2744 return; /* !!! USE NEW STUFF !!! */
2747 if (PANEL_DEACTIVATED(pos))
2750 if (use_dynamic_chars) /* use dynamic number of chars */
2752 chars = (value < 1000 ? chars1 : chars2);
2753 font_nr = (value < 1000 ? font1_nr : font2_nr);
2756 /* clear background if value just changed its size (dynamic chars only) */
2757 if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2759 int width1 = chars1 * getFontWidth(font1_nr);
2760 int width2 = chars2 * getFontWidth(font2_nr);
2761 int max_width = MAX(width1, width2);
2762 int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2764 pos->width = max_width;
2766 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2767 max_width, max_height);
2770 pos->width = chars * getFontWidth(font_nr);
2772 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2777 void DrawGameValue_Level(int value)
2779 struct TextPosInfo *pos = &game.panel.level_number;
2782 int chars = pos->size;
2784 int font1_nr = pos->font;
2785 int font2_nr = pos->font_alt;
2787 int font1_nr = FONT_TEXT_2;
2788 int font2_nr = FONT_TEXT_1;
2790 int font_nr = font1_nr;
2791 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2794 return; /* !!! USE NEW STUFF !!! */
2797 if (PANEL_DEACTIVATED(pos))
2800 if (use_dynamic_chars) /* use dynamic number of chars */
2802 chars = (level_nr < 100 ? chars1 : chars2);
2803 font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2806 pos->width = chars * getFontWidth(font_nr);
2808 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2811 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2814 struct TextPosInfo *pos = &game.panel.keys;
2817 int base_key_graphic = EL_KEY_1;
2822 return; /* !!! USE NEW STUFF !!! */
2826 if (PANEL_DEACTIVATED(pos))
2831 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2832 base_key_graphic = EL_EM_KEY_1;
2836 pos->width = 4 * MINI_TILEX;
2840 for (i = 0; i < MAX_NUM_KEYS; i++)
2842 /* currently only 4 of 8 possible keys are displayed */
2843 for (i = 0; i < STD_NUM_KEYS; i++)
2847 struct TextPosInfo *pos = &game.panel.key[i];
2849 int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2850 int src_y = DOOR_GFX_PAGEY1 + 123;
2852 int dst_x = PANEL_XPOS(pos);
2853 int dst_y = PANEL_YPOS(pos);
2855 int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2856 int dst_y = PANEL_YPOS(pos);
2860 int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2861 level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2863 int graphic = el2edimg(element);
2867 if (PANEL_DEACTIVATED(pos))
2872 /* masked blit with tiles from half-size scaled bitmap does not work yet
2873 (no mask bitmap created for these sizes after loading and scaling) --
2874 solution: load without creating mask, scale, then create final mask */
2876 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2877 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2882 int graphic = el2edimg(base_key_graphic + i);
2887 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2889 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2890 dst_x - src_x, dst_y - src_y);
2891 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2897 DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2899 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2900 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2903 DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
2905 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2906 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2914 void DrawGameValue_Emeralds(int value)
2916 int font_nr = FONT_TEXT_2;
2917 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2919 if (PANEL_DEACTIVATED(game.panel.gems))
2922 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
2925 void DrawGameValue_Dynamite(int value)
2927 int font_nr = FONT_TEXT_2;
2928 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2930 if (PANEL_DEACTIVATED(game.panel.inventory_count))
2933 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
2936 void DrawGameValue_Score(int value)
2938 int font_nr = FONT_TEXT_2;
2939 int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
2941 if (PANEL_DEACTIVATED(game.panel.score))
2944 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
2947 void DrawGameValue_Time(int value)
2949 int font1_nr = FONT_TEXT_2;
2951 int font2_nr = FONT_TEXT_1;
2953 int font2_nr = FONT_LEVEL_NUMBER;
2955 int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
2956 int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
2958 if (PANEL_DEACTIVATED(game.panel.time))
2961 /* clear background if value just changed its size */
2962 if (value == 999 || value == 1000)
2963 ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
2966 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
2968 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
2971 void DrawGameValue_Level(int value)
2973 int font1_nr = FONT_TEXT_2;
2975 int font2_nr = FONT_TEXT_1;
2977 int font2_nr = FONT_LEVEL_NUMBER;
2980 if (PANEL_DEACTIVATED(game.panel.level))
2984 DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
2986 DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
2989 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2991 int base_key_graphic = EL_KEY_1;
2994 if (PANEL_DEACTIVATED(game.panel.keys))
2997 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2998 base_key_graphic = EL_EM_KEY_1;
3000 /* currently only 4 of 8 possible keys are displayed */
3001 for (i = 0; i < STD_NUM_KEYS; i++)
3003 int x = XX_KEYS + i * MINI_TILEX;
3007 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3009 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3010 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3016 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3019 int key[MAX_NUM_KEYS];
3022 /* prevent EM engine from updating time/score values parallel to GameWon() */
3023 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3024 local_player->LevelSolved)
3027 for (i = 0; i < MAX_NUM_KEYS; i++)
3028 key[i] = key_bits & (1 << i);
3030 DrawGameValue_Level(level_nr);
3032 DrawGameValue_Emeralds(emeralds);
3033 DrawGameValue_Dynamite(dynamite);
3034 DrawGameValue_Score(score);
3035 DrawGameValue_Time(time);
3037 DrawGameValue_Keys(key);
3040 void UpdateGameDoorValues()
3042 UpdateGameControlValues();
3045 void DrawGameDoorValues()
3047 DisplayGameControlValues();
3050 void DrawGameDoorValues_OLD()
3052 int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
3053 int dynamite_value = 0;
3054 int score_value = (local_player->LevelSolved ? local_player->score_final :
3055 local_player->score);
3056 int gems_value = local_player->gems_still_needed;
3060 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3062 DrawGameDoorValues_EM();
3067 if (game.centered_player_nr == -1)
3069 for (i = 0; i < MAX_PLAYERS; i++)
3071 for (j = 0; j < MAX_NUM_KEYS; j++)
3072 if (stored_player[i].key[j])
3073 key_bits |= (1 << j);
3075 dynamite_value += stored_player[i].inventory_size;
3080 int player_nr = game.centered_player_nr;
3082 for (i = 0; i < MAX_NUM_KEYS; i++)
3083 if (stored_player[player_nr].key[i])
3084 key_bits |= (1 << i);
3086 dynamite_value = stored_player[player_nr].inventory_size;
3089 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3095 =============================================================================
3097 -----------------------------------------------------------------------------
3098 initialize game engine due to level / tape version number
3099 =============================================================================
3102 static void InitGameEngine()
3104 int i, j, k, l, x, y;
3106 /* set game engine from tape file when re-playing, else from level file */
3107 game.engine_version = (tape.playing ? tape.engine_version :
3108 level.game_version);
3110 /* ---------------------------------------------------------------------- */
3111 /* set flags for bugs and changes according to active game engine version */
3112 /* ---------------------------------------------------------------------- */
3115 Summary of bugfix/change:
3116 Fixed handling for custom elements that change when pushed by the player.
3118 Fixed/changed in version:
3122 Before 3.1.0, custom elements that "change when pushing" changed directly
3123 after the player started pushing them (until then handled in "DigField()").
3124 Since 3.1.0, these custom elements are not changed until the "pushing"
3125 move of the element is finished (now handled in "ContinueMoving()").
3127 Affected levels/tapes:
3128 The first condition is generally needed for all levels/tapes before version
3129 3.1.0, which might use the old behaviour before it was changed; known tapes
3130 that are affected are some tapes from the level set "Walpurgis Gardens" by
3132 The second condition is an exception from the above case and is needed for
3133 the special case of tapes recorded with game (not engine!) version 3.1.0 or
3134 above (including some development versions of 3.1.0), but before it was
3135 known that this change would break tapes like the above and was fixed in
3136 3.1.1, so that the changed behaviour was active although the engine version
3137 while recording maybe was before 3.1.0. There is at least one tape that is
3138 affected by this exception, which is the tape for the one-level set "Bug
3139 Machine" by Juergen Bonhagen.
3142 game.use_change_when_pushing_bug =
3143 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3145 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3146 tape.game_version < VERSION_IDENT(3,1,1,0)));
3149 Summary of bugfix/change:
3150 Fixed handling for blocking the field the player leaves when moving.
3152 Fixed/changed in version:
3156 Before 3.1.1, when "block last field when moving" was enabled, the field
3157 the player is leaving when moving was blocked for the time of the move,
3158 and was directly unblocked afterwards. This resulted in the last field
3159 being blocked for exactly one less than the number of frames of one player
3160 move. Additionally, even when blocking was disabled, the last field was
3161 blocked for exactly one frame.
3162 Since 3.1.1, due to changes in player movement handling, the last field
3163 is not blocked at all when blocking is disabled. When blocking is enabled,
3164 the last field is blocked for exactly the number of frames of one player
3165 move. Additionally, if the player is Murphy, the hero of Supaplex, the
3166 last field is blocked for exactly one more than the number of frames of
3169 Affected levels/tapes:
3170 (!!! yet to be determined -- probably many !!!)
3173 game.use_block_last_field_bug =
3174 (game.engine_version < VERSION_IDENT(3,1,1,0));
3177 Summary of bugfix/change:
3178 Changed behaviour of CE changes with multiple changes per single frame.
3180 Fixed/changed in version:
3184 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3185 This resulted in race conditions where CEs seem to behave strange in some
3186 situations (where triggered CE changes were just skipped because there was
3187 already a CE change on that tile in the playfield in that engine frame).
3188 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3189 (The number of changes per frame must be limited in any case, because else
3190 it is easily possible to define CE changes that would result in an infinite
3191 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3192 should be set large enough so that it would only be reached in cases where
3193 the corresponding CE change conditions run into a loop. Therefore, it seems
3194 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3195 maximal number of change pages for custom elements.)
3197 Affected levels/tapes:
3201 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3202 game.max_num_changes_per_frame = 1;
3204 game.max_num_changes_per_frame =
3205 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3208 /* ---------------------------------------------------------------------- */
3210 /* default scan direction: scan playfield from top/left to bottom/right */
3211 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3213 /* dynamically adjust element properties according to game engine version */
3214 InitElementPropertiesEngine(game.engine_version);
3217 printf("level %d: level version == %06d\n", level_nr, level.game_version);
3218 printf(" tape version == %06d [%s] [file: %06d]\n",
3219 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3221 printf(" => game.engine_version == %06d\n", game.engine_version);
3224 /* ---------- initialize player's initial move delay --------------------- */
3226 /* dynamically adjust player properties according to level information */
3227 for (i = 0; i < MAX_PLAYERS; i++)
3228 game.initial_move_delay_value[i] =
3229 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3231 /* dynamically adjust player properties according to game engine version */
3232 for (i = 0; i < MAX_PLAYERS; i++)
3233 game.initial_move_delay[i] =
3234 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3235 game.initial_move_delay_value[i] : 0);
3237 /* ---------- initialize player's initial push delay --------------------- */
3239 /* dynamically adjust player properties according to game engine version */
3240 game.initial_push_delay_value =
3241 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3243 /* ---------- initialize changing elements ------------------------------- */
3245 /* initialize changing elements information */
3246 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3248 struct ElementInfo *ei = &element_info[i];
3250 /* this pointer might have been changed in the level editor */
3251 ei->change = &ei->change_page[0];
3253 if (!IS_CUSTOM_ELEMENT(i))
3255 ei->change->target_element = EL_EMPTY_SPACE;
3256 ei->change->delay_fixed = 0;
3257 ei->change->delay_random = 0;
3258 ei->change->delay_frames = 1;
3261 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3263 ei->has_change_event[j] = FALSE;
3265 ei->event_page_nr[j] = 0;
3266 ei->event_page[j] = &ei->change_page[0];
3270 /* add changing elements from pre-defined list */
3271 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3273 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3274 struct ElementInfo *ei = &element_info[ch_delay->element];
3276 ei->change->target_element = ch_delay->target_element;
3277 ei->change->delay_fixed = ch_delay->change_delay;
3279 ei->change->pre_change_function = ch_delay->pre_change_function;
3280 ei->change->change_function = ch_delay->change_function;
3281 ei->change->post_change_function = ch_delay->post_change_function;
3283 ei->change->can_change = TRUE;
3284 ei->change->can_change_or_has_action = TRUE;
3286 ei->has_change_event[CE_DELAY] = TRUE;
3288 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3289 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3292 /* ---------- initialize internal run-time variables ------------- */
3294 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3296 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3298 for (j = 0; j < ei->num_change_pages; j++)
3300 ei->change_page[j].can_change_or_has_action =
3301 (ei->change_page[j].can_change |
3302 ei->change_page[j].has_action);
3306 /* add change events from custom element configuration */
3307 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3309 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3311 for (j = 0; j < ei->num_change_pages; j++)
3313 if (!ei->change_page[j].can_change_or_has_action)
3316 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3318 /* only add event page for the first page found with this event */
3319 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3321 ei->has_change_event[k] = TRUE;
3323 ei->event_page_nr[k] = j;
3324 ei->event_page[k] = &ei->change_page[j];
3330 /* ---------- initialize run-time trigger player and element ------------- */
3332 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3334 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3336 for (j = 0; j < ei->num_change_pages; j++)
3338 ei->change_page[j].actual_trigger_element = EL_EMPTY;
3339 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
3340 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_1;
3341 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3342 ei->change_page[j].actual_trigger_ce_value = 0;
3343 ei->change_page[j].actual_trigger_ce_score = 0;
3347 /* ---------- initialize trigger events ---------------------------------- */
3349 /* initialize trigger events information */
3350 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3351 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3352 trigger_events[i][j] = FALSE;
3354 /* add trigger events from element change event properties */
3355 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3357 struct ElementInfo *ei = &element_info[i];
3359 for (j = 0; j < ei->num_change_pages; j++)
3361 if (!ei->change_page[j].can_change_or_has_action)
3364 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3366 int trigger_element = ei->change_page[j].trigger_element;
3368 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3370 if (ei->change_page[j].has_event[k])
3372 if (IS_GROUP_ELEMENT(trigger_element))
3374 struct ElementGroupInfo *group =
3375 element_info[trigger_element].group;
3377 for (l = 0; l < group->num_elements_resolved; l++)
3378 trigger_events[group->element_resolved[l]][k] = TRUE;
3380 else if (trigger_element == EL_ANY_ELEMENT)
3381 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3382 trigger_events[l][k] = TRUE;
3384 trigger_events[trigger_element][k] = TRUE;
3391 /* ---------- initialize push delay -------------------------------------- */
3393 /* initialize push delay values to default */
3394 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3396 if (!IS_CUSTOM_ELEMENT(i))
3398 /* set default push delay values (corrected since version 3.0.7-1) */
3399 if (game.engine_version < VERSION_IDENT(3,0,7,1))
3401 element_info[i].push_delay_fixed = 2;
3402 element_info[i].push_delay_random = 8;
3406 element_info[i].push_delay_fixed = 8;
3407 element_info[i].push_delay_random = 8;
3412 /* set push delay value for certain elements from pre-defined list */
3413 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3415 int e = push_delay_list[i].element;
3417 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
3418 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3421 /* set push delay value for Supaplex elements for newer engine versions */
3422 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3424 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3426 if (IS_SP_ELEMENT(i))
3428 /* set SP push delay to just enough to push under a falling zonk */
3429 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3431 element_info[i].push_delay_fixed = delay;
3432 element_info[i].push_delay_random = 0;
3437 /* ---------- initialize move stepsize ----------------------------------- */
3439 /* initialize move stepsize values to default */
3440 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3441 if (!IS_CUSTOM_ELEMENT(i))
3442 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3444 /* set move stepsize value for certain elements from pre-defined list */
3445 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3447 int e = move_stepsize_list[i].element;
3449 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3452 /* ---------- initialize collect score ----------------------------------- */
3454 /* initialize collect score values for custom elements from initial value */
3455 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3456 if (IS_CUSTOM_ELEMENT(i))
3457 element_info[i].collect_score = element_info[i].collect_score_initial;
3459 /* ---------- initialize collect count ----------------------------------- */
3461 /* initialize collect count values for non-custom elements */
3462 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3463 if (!IS_CUSTOM_ELEMENT(i))
3464 element_info[i].collect_count_initial = 0;
3466 /* add collect count values for all elements from pre-defined list */
3467 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3468 element_info[collect_count_list[i].element].collect_count_initial =
3469 collect_count_list[i].count;
3471 /* ---------- initialize access direction -------------------------------- */
3473 /* initialize access direction values to default (access from every side) */
3474 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3475 if (!IS_CUSTOM_ELEMENT(i))
3476 element_info[i].access_direction = MV_ALL_DIRECTIONS;
3478 /* set access direction value for certain elements from pre-defined list */
3479 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3480 element_info[access_direction_list[i].element].access_direction =
3481 access_direction_list[i].direction;
3483 /* ---------- initialize explosion content ------------------------------- */
3484 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3486 if (IS_CUSTOM_ELEMENT(i))
3489 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3491 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3493 element_info[i].content.e[x][y] =
3494 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3495 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3496 i == EL_PLAYER_3 ? EL_EMERALD :
3497 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3498 i == EL_MOLE ? EL_EMERALD_RED :
3499 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3500 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3501 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3502 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3503 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3504 i == EL_WALL_EMERALD ? EL_EMERALD :
3505 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3506 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3507 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3508 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3509 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3510 i == EL_WALL_PEARL ? EL_PEARL :
3511 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3516 /* ---------- initialize recursion detection ------------------------------ */
3517 recursion_loop_depth = 0;
3518 recursion_loop_detected = FALSE;
3519 recursion_loop_element = EL_UNDEFINED;
3521 /* ---------- initialize graphics engine ---------------------------------- */
3522 game.scroll_delay_value =
3523 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3524 setup.scroll_delay ? setup.scroll_delay_value : 0);
3525 game.scroll_delay_value =
3526 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3529 int get_num_special_action(int element, int action_first, int action_last)
3531 int num_special_action = 0;
3534 for (i = action_first; i <= action_last; i++)
3536 boolean found = FALSE;
3538 for (j = 0; j < NUM_DIRECTIONS; j++)
3539 if (el_act_dir2img(element, i, j) !=
3540 el_act_dir2img(element, ACTION_DEFAULT, j))
3544 num_special_action++;
3549 return num_special_action;
3554 =============================================================================
3556 -----------------------------------------------------------------------------
3557 initialize and start new game
3558 =============================================================================
3563 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3564 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3565 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3567 boolean do_fading = (game_status == GAME_MODE_MAIN);
3571 game_status = GAME_MODE_PLAYING;
3574 InitGameControlValues();
3576 /* don't play tapes over network */
3577 network_playing = (options.network && !tape.playing);
3579 for (i = 0; i < MAX_PLAYERS; i++)
3581 struct PlayerInfo *player = &stored_player[i];
3583 player->index_nr = i;
3584 player->index_bit = (1 << i);
3585 player->element_nr = EL_PLAYER_1 + i;
3587 player->present = FALSE;
3588 player->active = FALSE;
3589 player->killed = FALSE;
3592 player->effective_action = 0;
3593 player->programmed_action = 0;
3596 player->score_final = 0;
3598 player->gems_still_needed = level.gems_needed;
3599 player->sokobanfields_still_needed = 0;
3600 player->lights_still_needed = 0;
3601 player->friends_still_needed = 0;
3603 for (j = 0; j < MAX_NUM_KEYS; j++)
3604 player->key[j] = FALSE;
3606 player->num_white_keys = 0;
3608 player->dynabomb_count = 0;
3609 player->dynabomb_size = 1;
3610 player->dynabombs_left = 0;
3611 player->dynabomb_xl = FALSE;
3613 player->MovDir = MV_NONE;
3616 player->GfxDir = MV_NONE;
3617 player->GfxAction = ACTION_DEFAULT;
3619 player->StepFrame = 0;
3621 player->use_murphy = FALSE;
3622 player->artwork_element =
3623 (level.use_artwork_element[i] ? level.artwork_element[i] :
3624 player->element_nr);
3626 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3627 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3629 player->gravity = level.initial_player_gravity[i];
3631 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3633 player->actual_frame_counter = 0;
3635 player->step_counter = 0;
3637 player->last_move_dir = MV_NONE;
3639 player->is_active = FALSE;
3641 player->is_waiting = FALSE;
3642 player->is_moving = FALSE;
3643 player->is_auto_moving = FALSE;
3644 player->is_digging = FALSE;
3645 player->is_snapping = FALSE;
3646 player->is_collecting = FALSE;
3647 player->is_pushing = FALSE;
3648 player->is_switching = FALSE;
3649 player->is_dropping = FALSE;
3650 player->is_dropping_pressed = FALSE;
3652 player->is_bored = FALSE;
3653 player->is_sleeping = FALSE;
3655 player->frame_counter_bored = -1;
3656 player->frame_counter_sleeping = -1;
3658 player->anim_delay_counter = 0;
3659 player->post_delay_counter = 0;
3661 player->dir_waiting = MV_NONE;
3662 player->action_waiting = ACTION_DEFAULT;
3663 player->last_action_waiting = ACTION_DEFAULT;
3664 player->special_action_bored = ACTION_DEFAULT;
3665 player->special_action_sleeping = ACTION_DEFAULT;
3667 player->switch_x = -1;
3668 player->switch_y = -1;
3670 player->drop_x = -1;
3671 player->drop_y = -1;
3673 player->show_envelope = 0;
3675 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3677 player->push_delay = -1; /* initialized when pushing starts */
3678 player->push_delay_value = game.initial_push_delay_value;
3680 player->drop_delay = 0;
3681 player->drop_pressed_delay = 0;
3683 player->last_jx = -1;
3684 player->last_jy = -1;
3688 player->shield_normal_time_left = 0;
3689 player->shield_deadly_time_left = 0;
3691 player->inventory_infinite_element = EL_UNDEFINED;
3692 player->inventory_size = 0;
3694 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3695 SnapField(player, 0, 0);
3697 player->LevelSolved = FALSE;
3698 player->GameOver = FALSE;
3700 player->LevelSolved_GameWon = FALSE;
3701 player->LevelSolved_GameEnd = FALSE;
3702 player->LevelSolved_PanelOff = FALSE;
3703 player->LevelSolved_SaveTape = FALSE;
3704 player->LevelSolved_SaveScore = FALSE;
3705 player->LevelSolved_CountingTime = 0;
3706 player->LevelSolved_CountingScore = 0;
3709 network_player_action_received = FALSE;
3711 #if defined(NETWORK_AVALIABLE)
3712 /* initial null action */
3713 if (network_playing)
3714 SendToServer_MovePlayer(MV_NONE);
3723 TimeLeft = level.time;
3726 ScreenMovDir = MV_NONE;
3730 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3732 AllPlayersGone = FALSE;
3734 game.yamyam_content_nr = 0;
3735 game.robot_wheel_active = FALSE;
3736 game.magic_wall_active = FALSE;
3737 game.magic_wall_time_left = 0;
3738 game.light_time_left = 0;
3739 game.timegate_time_left = 0;
3740 game.switchgate_pos = 0;
3741 game.wind_direction = level.wind_direction_initial;
3743 #if !USE_PLAYER_GRAVITY
3744 game.gravity = FALSE;
3745 game.explosions_delayed = TRUE;
3748 game.lenses_time_left = 0;
3749 game.magnify_time_left = 0;
3751 game.ball_state = level.ball_state_initial;
3752 game.ball_content_nr = 0;
3754 game.envelope_active = FALSE;
3756 /* set focus to local player for network games, else to all players */
3757 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3758 game.centered_player_nr_next = game.centered_player_nr;
3759 game.set_centered_player = FALSE;
3761 if (network_playing && tape.recording)
3763 /* store client dependent player focus when recording network games */
3764 tape.centered_player_nr_next = game.centered_player_nr_next;
3765 tape.set_centered_player = TRUE;
3768 for (i = 0; i < NUM_BELTS; i++)
3770 game.belt_dir[i] = MV_NONE;
3771 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3774 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3775 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3777 SCAN_PLAYFIELD(x, y)
3779 Feld[x][y] = level.field[x][y];
3780 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3781 ChangeDelay[x][y] = 0;
3782 ChangePage[x][y] = -1;
3783 #if USE_NEW_CUSTOM_VALUE
3784 CustomValue[x][y] = 0; /* initialized in InitField() */
3786 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3788 WasJustMoving[x][y] = 0;
3789 WasJustFalling[x][y] = 0;
3790 CheckCollision[x][y] = 0;
3791 CheckImpact[x][y] = 0;
3793 Pushed[x][y] = FALSE;
3795 ChangeCount[x][y] = 0;
3796 ChangeEvent[x][y] = -1;
3798 ExplodePhase[x][y] = 0;
3799 ExplodeDelay[x][y] = 0;
3800 ExplodeField[x][y] = EX_TYPE_NONE;
3802 RunnerVisit[x][y] = 0;
3803 PlayerVisit[x][y] = 0;
3806 GfxRandom[x][y] = INIT_GFX_RANDOM();
3807 GfxElement[x][y] = EL_UNDEFINED;
3808 GfxAction[x][y] = ACTION_DEFAULT;
3809 GfxDir[x][y] = MV_NONE;
3812 SCAN_PLAYFIELD(x, y)
3814 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3816 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3818 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3821 InitField(x, y, TRUE);
3823 ResetGfxAnimation(x, y);
3828 for (i = 0; i < MAX_PLAYERS; i++)
3830 struct PlayerInfo *player = &stored_player[i];
3832 /* set number of special actions for bored and sleeping animation */
3833 player->num_special_action_bored =
3834 get_num_special_action(player->artwork_element,
3835 ACTION_BORING_1, ACTION_BORING_LAST);
3836 player->num_special_action_sleeping =
3837 get_num_special_action(player->artwork_element,
3838 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3841 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3842 emulate_sb ? EMU_SOKOBAN :
3843 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3845 #if USE_NEW_ALL_SLIPPERY
3846 /* initialize type of slippery elements */
3847 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3849 if (!IS_CUSTOM_ELEMENT(i))
3851 /* default: elements slip down either to the left or right randomly */
3852 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3854 /* SP style elements prefer to slip down on the left side */
3855 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3856 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3858 /* BD style elements prefer to slip down on the left side */
3859 if (game.emulation == EMU_BOULDERDASH)
3860 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3865 /* initialize explosion and ignition delay */
3866 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3868 if (!IS_CUSTOM_ELEMENT(i))
3871 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3872 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3873 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3874 int last_phase = (num_phase + 1) * delay;
3875 int half_phase = (num_phase / 2) * delay;
3877 element_info[i].explosion_delay = last_phase - 1;
3878 element_info[i].ignition_delay = half_phase;
3880 if (i == EL_BLACK_ORB)
3881 element_info[i].ignition_delay = 1;
3885 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
3886 element_info[i].explosion_delay = 1;
3888 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
3889 element_info[i].ignition_delay = 1;
3893 /* correct non-moving belts to start moving left */
3894 for (i = 0; i < NUM_BELTS; i++)
3895 if (game.belt_dir[i] == MV_NONE)
3896 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3898 /* check if any connected player was not found in playfield */
3899 for (i = 0; i < MAX_PLAYERS; i++)
3901 struct PlayerInfo *player = &stored_player[i];
3903 if (player->connected && !player->present)
3905 for (j = 0; j < MAX_PLAYERS; j++)
3907 struct PlayerInfo *some_player = &stored_player[j];
3908 int jx = some_player->jx, jy = some_player->jy;
3910 /* assign first free player found that is present in the playfield */
3911 if (some_player->present && !some_player->connected)
3913 player->present = TRUE;
3914 player->active = TRUE;
3916 some_player->present = FALSE;
3917 some_player->active = FALSE;
3919 player->artwork_element = some_player->artwork_element;
3921 player->block_last_field = some_player->block_last_field;
3922 player->block_delay_adjustment = some_player->block_delay_adjustment;
3924 StorePlayer[jx][jy] = player->element_nr;
3925 player->jx = player->last_jx = jx;
3926 player->jy = player->last_jy = jy;
3936 /* when playing a tape, eliminate all players who do not participate */
3938 for (i = 0; i < MAX_PLAYERS; i++)
3940 if (stored_player[i].active && !tape.player_participates[i])
3942 struct PlayerInfo *player = &stored_player[i];
3943 int jx = player->jx, jy = player->jy;
3945 player->active = FALSE;
3946 StorePlayer[jx][jy] = 0;
3947 Feld[jx][jy] = EL_EMPTY;
3951 else if (!options.network && !setup.team_mode) /* && !tape.playing */
3953 /* when in single player mode, eliminate all but the first active player */
3955 for (i = 0; i < MAX_PLAYERS; i++)
3957 if (stored_player[i].active)
3959 for (j = i + 1; j < MAX_PLAYERS; j++)
3961 if (stored_player[j].active)
3963 struct PlayerInfo *player = &stored_player[j];
3964 int jx = player->jx, jy = player->jy;
3966 player->active = FALSE;
3967 player->present = FALSE;
3969 StorePlayer[jx][jy] = 0;
3970 Feld[jx][jy] = EL_EMPTY;
3977 /* when recording the game, store which players take part in the game */
3980 for (i = 0; i < MAX_PLAYERS; i++)
3981 if (stored_player[i].active)
3982 tape.player_participates[i] = TRUE;
3987 for (i = 0; i < MAX_PLAYERS; i++)
3989 struct PlayerInfo *player = &stored_player[i];
3991 printf("Player %d: present == %d, connected == %d, active == %d.\n",
3996 if (local_player == player)
3997 printf("Player %d is local player.\n", i+1);
4001 if (BorderElement == EL_EMPTY)
4004 SBX_Right = lev_fieldx - SCR_FIELDX;
4006 SBY_Lower = lev_fieldy - SCR_FIELDY;
4011 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4013 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4016 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4017 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4019 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4020 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4022 /* if local player not found, look for custom element that might create
4023 the player (make some assumptions about the right custom element) */
4024 if (!local_player->present)
4026 int start_x = 0, start_y = 0;
4027 int found_rating = 0;
4028 int found_element = EL_UNDEFINED;
4029 int player_nr = local_player->index_nr;
4031 SCAN_PLAYFIELD(x, y)
4033 int element = Feld[x][y];
4038 if (level.use_start_element[player_nr] &&
4039 level.start_element[player_nr] == element &&
4046 found_element = element;
4049 if (!IS_CUSTOM_ELEMENT(element))
4052 if (CAN_CHANGE(element))
4054 for (i = 0; i < element_info[element].num_change_pages; i++)
4056 /* check for player created from custom element as single target */
4057 content = element_info[element].change_page[i].target_element;
4058 is_player = ELEM_IS_PLAYER(content);
4060 if (is_player && (found_rating < 3 ||
4061 (found_rating == 3 && element < found_element)))
4067 found_element = element;
4072 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4074 /* check for player created from custom element as explosion content */
4075 content = element_info[element].content.e[xx][yy];
4076 is_player = ELEM_IS_PLAYER(content);
4078 if (is_player && (found_rating < 2 ||
4079 (found_rating == 2 && element < found_element)))
4081 start_x = x + xx - 1;
4082 start_y = y + yy - 1;
4085 found_element = element;
4088 if (!CAN_CHANGE(element))
4091 for (i = 0; i < element_info[element].num_change_pages; i++)
4093 /* check for player created from custom element as extended target */
4095 element_info[element].change_page[i].target_content.e[xx][yy];
4097 is_player = ELEM_IS_PLAYER(content);
4099 if (is_player && (found_rating < 1 ||
4100 (found_rating == 1 && element < found_element)))
4102 start_x = x + xx - 1;
4103 start_y = y + yy - 1;
4106 found_element = element;
4112 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
4113 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4116 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4117 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4122 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
4123 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4124 local_player->jx - MIDPOSX);
4126 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4127 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4128 local_player->jy - MIDPOSY);
4132 /* do not use PLAYING mask for fading out from main screen */
4133 game_status = GAME_MODE_MAIN;
4138 if (!game.restart_level)
4139 CloseDoor(DOOR_CLOSE_1);
4142 if (level_editor_test_game)
4143 FadeSkipNextFadeIn();
4145 FadeSetEnterScreen();
4147 if (level_editor_test_game)
4148 fading = fading_none;
4150 fading = menu.destination;
4154 FadeOut(REDRAW_FIELD);
4157 FadeOut(REDRAW_FIELD);
4161 game_status = GAME_MODE_PLAYING;
4164 /* !!! FIX THIS (START) !!! */
4165 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4167 InitGameEngine_EM();
4169 /* blit playfield from scroll buffer to normal back buffer for fading in */
4170 BlitScreenToBitmap_EM(backbuffer);
4177 /* after drawing the level, correct some elements */
4178 if (game.timegate_time_left == 0)
4179 CloseAllOpenTimegates();
4181 /* blit playfield from scroll buffer to normal back buffer for fading in */
4182 if (setup.soft_scrolling)
4183 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4185 redraw_mask |= REDRAW_FROM_BACKBUFFER;
4187 /* !!! FIX THIS (END) !!! */
4190 FadeIn(REDRAW_FIELD);
4193 FadeIn(REDRAW_FIELD);
4198 if (!game.restart_level)
4200 /* copy default game door content to main double buffer */
4201 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4202 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4205 SetPanelBackground();
4206 SetDrawBackgroundMask(REDRAW_DOOR_1);
4208 UpdateGameDoorValues();
4209 DrawGameDoorValues();
4211 if (!game.restart_level)
4215 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4216 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4217 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4221 /* copy actual game door content to door double buffer for OpenDoor() */
4222 BlitBitmap(drawto, bitmap_db_door,
4223 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4225 OpenDoor(DOOR_OPEN_ALL);
4227 PlaySound(SND_GAME_STARTING);
4229 if (setup.sound_music)
4232 KeyboardAutoRepeatOffUnlessAutoplay();
4236 for (i = 0; i < MAX_PLAYERS; i++)
4237 printf("Player %d %sactive.\n",
4238 i + 1, (stored_player[i].active ? "" : "not "));
4249 game.restart_level = FALSE;
4252 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4254 /* this is used for non-R'n'D game engines to update certain engine values */
4256 /* needed to determine if sounds are played within the visible screen area */
4257 scroll_x = actual_scroll_x;
4258 scroll_y = actual_scroll_y;
4261 void InitMovDir(int x, int y)
4263 int i, element = Feld[x][y];
4264 static int xy[4][2] =
4271 static int direction[3][4] =
4273 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4274 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4275 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4284 Feld[x][y] = EL_BUG;
4285 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4288 case EL_SPACESHIP_RIGHT:
4289 case EL_SPACESHIP_UP:
4290 case EL_SPACESHIP_LEFT:
4291 case EL_SPACESHIP_DOWN:
4292 Feld[x][y] = EL_SPACESHIP;
4293 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4296 case EL_BD_BUTTERFLY_RIGHT:
4297 case EL_BD_BUTTERFLY_UP:
4298 case EL_BD_BUTTERFLY_LEFT:
4299 case EL_BD_BUTTERFLY_DOWN:
4300 Feld[x][y] = EL_BD_BUTTERFLY;
4301 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4304 case EL_BD_FIREFLY_RIGHT:
4305 case EL_BD_FIREFLY_UP:
4306 case EL_BD_FIREFLY_LEFT:
4307 case EL_BD_FIREFLY_DOWN:
4308 Feld[x][y] = EL_BD_FIREFLY;
4309 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4312 case EL_PACMAN_RIGHT:
4314 case EL_PACMAN_LEFT:
4315 case EL_PACMAN_DOWN:
4316 Feld[x][y] = EL_PACMAN;
4317 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4320 case EL_YAMYAM_LEFT:
4321 case EL_YAMYAM_RIGHT:
4323 case EL_YAMYAM_DOWN:
4324 Feld[x][y] = EL_YAMYAM;
4325 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4328 case EL_SP_SNIKSNAK:
4329 MovDir[x][y] = MV_UP;
4332 case EL_SP_ELECTRON:
4333 MovDir[x][y] = MV_LEFT;
4340 Feld[x][y] = EL_MOLE;
4341 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4345 if (IS_CUSTOM_ELEMENT(element))
4347 struct ElementInfo *ei = &element_info[element];
4348 int move_direction_initial = ei->move_direction_initial;
4349 int move_pattern = ei->move_pattern;
4351 if (move_direction_initial == MV_START_PREVIOUS)
4353 if (MovDir[x][y] != MV_NONE)
4356 move_direction_initial = MV_START_AUTOMATIC;
4359 if (move_direction_initial == MV_START_RANDOM)
4360 MovDir[x][y] = 1 << RND(4);
4361 else if (move_direction_initial & MV_ANY_DIRECTION)
4362 MovDir[x][y] = move_direction_initial;
4363 else if (move_pattern == MV_ALL_DIRECTIONS ||
4364 move_pattern == MV_TURNING_LEFT ||
4365 move_pattern == MV_TURNING_RIGHT ||
4366 move_pattern == MV_TURNING_LEFT_RIGHT ||
4367 move_pattern == MV_TURNING_RIGHT_LEFT ||
4368 move_pattern == MV_TURNING_RANDOM)
4369 MovDir[x][y] = 1 << RND(4);
4370 else if (move_pattern == MV_HORIZONTAL)
4371 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4372 else if (move_pattern == MV_VERTICAL)
4373 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4374 else if (move_pattern & MV_ANY_DIRECTION)
4375 MovDir[x][y] = element_info[element].move_pattern;
4376 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4377 move_pattern == MV_ALONG_RIGHT_SIDE)
4379 /* use random direction as default start direction */
4380 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4381 MovDir[x][y] = 1 << RND(4);
4383 for (i = 0; i < NUM_DIRECTIONS; i++)
4385 int x1 = x + xy[i][0];
4386 int y1 = y + xy[i][1];
4388 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4390 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4391 MovDir[x][y] = direction[0][i];
4393 MovDir[x][y] = direction[1][i];
4402 MovDir[x][y] = 1 << RND(4);
4404 if (element != EL_BUG &&
4405 element != EL_SPACESHIP &&
4406 element != EL_BD_BUTTERFLY &&
4407 element != EL_BD_FIREFLY)
4410 for (i = 0; i < NUM_DIRECTIONS; i++)
4412 int x1 = x + xy[i][0];
4413 int y1 = y + xy[i][1];
4415 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4417 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4419 MovDir[x][y] = direction[0][i];
4422 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4423 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4425 MovDir[x][y] = direction[1][i];
4434 GfxDir[x][y] = MovDir[x][y];
4437 void InitAmoebaNr(int x, int y)
4440 int group_nr = AmoebeNachbarNr(x, y);
4444 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4446 if (AmoebaCnt[i] == 0)
4454 AmoebaNr[x][y] = group_nr;
4455 AmoebaCnt[group_nr]++;
4456 AmoebaCnt2[group_nr]++;
4459 static void PlayerWins(struct PlayerInfo *player)
4461 player->LevelSolved = TRUE;
4462 player->GameOver = TRUE;
4464 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4465 level.native_em_level->lev->score : player->score);
4467 player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4468 player->LevelSolved_CountingScore = player->score_final;
4473 static int time, time_final;
4474 static int score, score_final;
4475 static int game_over_delay_1 = 0;
4476 static int game_over_delay_2 = 0;
4477 int game_over_delay_value_1 = 50;
4478 int game_over_delay_value_2 = 50;
4480 if (!local_player->LevelSolved_GameWon)
4484 /* do not start end game actions before the player stops moving (to exit) */
4485 if (local_player->MovPos)
4488 local_player->LevelSolved_GameWon = TRUE;
4489 local_player->LevelSolved_SaveTape = tape.recording;
4490 local_player->LevelSolved_SaveScore = !tape.playing;
4492 if (tape.auto_play) /* tape might already be stopped here */
4493 tape.auto_play_level_solved = TRUE;
4499 game_over_delay_1 = game_over_delay_value_1;
4500 game_over_delay_2 = game_over_delay_value_2;
4502 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4503 score = score_final = local_player->score_final;
4508 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4510 else if (level.time == 0 && TimePlayed < 999)
4513 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4516 local_player->score_final = score_final;
4518 if (level_editor_test_game)
4521 score = score_final;
4524 local_player->LevelSolved_CountingTime = time;
4525 local_player->LevelSolved_CountingScore = score;
4527 game_panel_controls[GAME_PANEL_TIME].value = time;
4528 game_panel_controls[GAME_PANEL_SCORE].value = score;
4530 DisplayGameControlValues();
4532 DrawGameValue_Time(time);
4533 DrawGameValue_Score(score);
4537 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4539 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4541 /* close exit door after last player */
4542 if ((AllPlayersGone &&
4543 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4544 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4545 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4546 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4547 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4549 int element = Feld[ExitX][ExitY];
4552 if (element == EL_EM_EXIT_OPEN ||
4553 element == EL_EM_STEEL_EXIT_OPEN)
4560 Feld[ExitX][ExitY] =
4561 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4562 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4563 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4564 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4565 EL_EM_STEEL_EXIT_CLOSING);
4567 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4571 /* player disappears */
4572 DrawLevelField(ExitX, ExitY);
4575 for (i = 0; i < MAX_PLAYERS; i++)
4577 struct PlayerInfo *player = &stored_player[i];
4579 if (player->present)
4581 RemovePlayer(player);
4583 /* player disappears */
4584 DrawLevelField(player->jx, player->jy);
4589 PlaySound(SND_GAME_WINNING);
4592 if (game_over_delay_1 > 0)
4594 game_over_delay_1--;
4599 if (time != time_final)
4601 int time_to_go = ABS(time_final - time);
4602 int time_count_dir = (time < time_final ? +1 : -1);
4603 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4605 time += time_count_steps * time_count_dir;
4606 score += time_count_steps * level.score[SC_TIME_BONUS];
4609 local_player->LevelSolved_CountingTime = time;
4610 local_player->LevelSolved_CountingScore = score;
4612 game_panel_controls[GAME_PANEL_TIME].value = time;
4613 game_panel_controls[GAME_PANEL_SCORE].value = score;
4615 DisplayGameControlValues();
4617 DrawGameValue_Time(time);
4618 DrawGameValue_Score(score);
4621 if (time == time_final)
4622 StopSound(SND_GAME_LEVELTIME_BONUS);
4623 else if (setup.sound_loops)
4624 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4626 PlaySound(SND_GAME_LEVELTIME_BONUS);
4631 local_player->LevelSolved_PanelOff = TRUE;
4633 if (game_over_delay_2 > 0)
4635 game_over_delay_2--;
4648 boolean raise_level = FALSE;
4650 local_player->LevelSolved_GameEnd = TRUE;
4652 CloseDoor(DOOR_CLOSE_1);
4654 if (local_player->LevelSolved_SaveTape)
4661 SaveTapeChecked(tape.level_nr); /* ask to save tape */
4663 SaveTape(tape.level_nr); /* ask to save tape */
4667 if (level_editor_test_game)
4669 game_status = GAME_MODE_MAIN;
4672 DrawAndFadeInMainMenu(REDRAW_FIELD);
4680 if (!local_player->LevelSolved_SaveScore)
4683 FadeOut(REDRAW_FIELD);
4686 game_status = GAME_MODE_MAIN;
4688 DrawAndFadeInMainMenu(REDRAW_FIELD);
4693 if (level_nr == leveldir_current->handicap_level)
4695 leveldir_current->handicap_level++;
4696 SaveLevelSetup_SeriesInfo();
4699 if (level_nr < leveldir_current->last_level)
4700 raise_level = TRUE; /* advance to next level */
4702 if ((hi_pos = NewHiScore()) >= 0)
4704 game_status = GAME_MODE_SCORES;
4706 DrawHallOfFame(hi_pos);
4717 FadeOut(REDRAW_FIELD);
4720 game_status = GAME_MODE_MAIN;
4728 DrawAndFadeInMainMenu(REDRAW_FIELD);
4737 LoadScore(level_nr);
4739 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4740 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4743 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4745 if (local_player->score_final > highscore[k].Score)
4747 /* player has made it to the hall of fame */
4749 if (k < MAX_SCORE_ENTRIES - 1)
4751 int m = MAX_SCORE_ENTRIES - 1;
4754 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4755 if (strEqual(setup.player_name, highscore[l].Name))
4757 if (m == k) /* player's new highscore overwrites his old one */
4761 for (l = m; l > k; l--)
4763 strcpy(highscore[l].Name, highscore[l - 1].Name);
4764 highscore[l].Score = highscore[l - 1].Score;
4771 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4772 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4773 highscore[k].Score = local_player->score_final;
4779 else if (!strncmp(setup.player_name, highscore[k].Name,
4780 MAX_PLAYER_NAME_LEN))
4781 break; /* player already there with a higher score */
4787 SaveScore(level_nr);
4792 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4794 int element = Feld[x][y];
4795 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4796 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4797 int horiz_move = (dx != 0);
4798 int sign = (horiz_move ? dx : dy);
4799 int step = sign * element_info[element].move_stepsize;
4801 /* special values for move stepsize for spring and things on conveyor belt */
4804 if (CAN_FALL(element) &&
4805 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4806 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4807 else if (element == EL_SPRING)
4808 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4814 inline static int getElementMoveStepsize(int x, int y)
4816 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4819 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4821 if (player->GfxAction != action || player->GfxDir != dir)
4824 printf("Player frame reset! (%d => %d, %d => %d)\n",
4825 player->GfxAction, action, player->GfxDir, dir);
4828 player->GfxAction = action;
4829 player->GfxDir = dir;
4831 player->StepFrame = 0;
4835 #if USE_GFX_RESET_GFX_ANIMATION
4836 static void ResetGfxFrame(int x, int y, boolean redraw)
4838 int element = Feld[x][y];
4839 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4840 int last_gfx_frame = GfxFrame[x][y];
4842 if (graphic_info[graphic].anim_global_sync)
4843 GfxFrame[x][y] = FrameCounter;
4844 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4845 GfxFrame[x][y] = CustomValue[x][y];
4846 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4847 GfxFrame[x][y] = element_info[element].collect_score;
4848 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4849 GfxFrame[x][y] = ChangeDelay[x][y];
4851 if (redraw && GfxFrame[x][y] != last_gfx_frame)
4852 DrawLevelGraphicAnimation(x, y, graphic);
4856 static void ResetGfxAnimation(int x, int y)
4858 GfxAction[x][y] = ACTION_DEFAULT;
4859 GfxDir[x][y] = MovDir[x][y];
4862 #if USE_GFX_RESET_GFX_ANIMATION
4863 ResetGfxFrame(x, y, FALSE);
4867 static void ResetRandomAnimationValue(int x, int y)
4869 GfxRandom[x][y] = INIT_GFX_RANDOM();
4872 void InitMovingField(int x, int y, int direction)
4874 int element = Feld[x][y];
4875 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4876 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4879 boolean is_moving_before, is_moving_after;
4881 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
4884 /* check if element was/is moving or being moved before/after mode change */
4887 is_moving_before = (WasJustMoving[x][y] != 0);
4889 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
4890 is_moving_before = WasJustMoving[x][y];
4893 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
4895 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
4897 /* reset animation only for moving elements which change direction of moving
4898 or which just started or stopped moving
4899 (else CEs with property "can move" / "not moving" are reset each frame) */
4900 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4902 if (is_moving_before != is_moving_after ||
4903 direction != MovDir[x][y])
4904 ResetGfxAnimation(x, y);
4906 if ((is_moving_before || is_moving_after) && !continues_moving)
4907 ResetGfxAnimation(x, y);
4910 if (!continues_moving)
4911 ResetGfxAnimation(x, y);
4914 MovDir[x][y] = direction;
4915 GfxDir[x][y] = direction;
4917 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4918 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4919 direction == MV_DOWN && CAN_FALL(element) ?
4920 ACTION_FALLING : ACTION_MOVING);
4922 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
4923 ACTION_FALLING : ACTION_MOVING);
4926 /* this is needed for CEs with property "can move" / "not moving" */
4928 if (is_moving_after)
4930 if (Feld[newx][newy] == EL_EMPTY)
4931 Feld[newx][newy] = EL_BLOCKED;
4933 MovDir[newx][newy] = MovDir[x][y];
4935 #if USE_NEW_CUSTOM_VALUE
4936 CustomValue[newx][newy] = CustomValue[x][y];
4939 GfxFrame[newx][newy] = GfxFrame[x][y];
4940 GfxRandom[newx][newy] = GfxRandom[x][y];
4941 GfxAction[newx][newy] = GfxAction[x][y];
4942 GfxDir[newx][newy] = GfxDir[x][y];
4946 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4948 int direction = MovDir[x][y];
4949 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4950 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
4956 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4958 int oldx = x, oldy = y;
4959 int direction = MovDir[x][y];
4961 if (direction == MV_LEFT)
4963 else if (direction == MV_RIGHT)
4965 else if (direction == MV_UP)
4967 else if (direction == MV_DOWN)
4970 *comes_from_x = oldx;
4971 *comes_from_y = oldy;
4974 int MovingOrBlocked2Element(int x, int y)
4976 int element = Feld[x][y];
4978 if (element == EL_BLOCKED)
4982 Blocked2Moving(x, y, &oldx, &oldy);
4983 return Feld[oldx][oldy];
4989 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4991 /* like MovingOrBlocked2Element(), but if element is moving
4992 and (x,y) is the field the moving element is just leaving,
4993 return EL_BLOCKED instead of the element value */
4994 int element = Feld[x][y];
4996 if (IS_MOVING(x, y))
4998 if (element == EL_BLOCKED)
5002 Blocked2Moving(x, y, &oldx, &oldy);
5003 return Feld[oldx][oldy];
5012 static void RemoveField(int x, int y)
5014 Feld[x][y] = EL_EMPTY;
5020 #if USE_NEW_CUSTOM_VALUE
5021 CustomValue[x][y] = 0;
5025 ChangeDelay[x][y] = 0;
5026 ChangePage[x][y] = -1;
5027 Pushed[x][y] = FALSE;
5030 ExplodeField[x][y] = EX_TYPE_NONE;
5033 GfxElement[x][y] = EL_UNDEFINED;
5034 GfxAction[x][y] = ACTION_DEFAULT;
5035 GfxDir[x][y] = MV_NONE;
5038 void RemoveMovingField(int x, int y)
5040 int oldx = x, oldy = y, newx = x, newy = y;
5041 int element = Feld[x][y];
5042 int next_element = EL_UNDEFINED;
5044 if (element != EL_BLOCKED && !IS_MOVING(x, y))
5047 if (IS_MOVING(x, y))
5049 Moving2Blocked(x, y, &newx, &newy);
5051 if (Feld[newx][newy] != EL_BLOCKED)
5053 /* element is moving, but target field is not free (blocked), but
5054 already occupied by something different (example: acid pool);
5055 in this case, only remove the moving field, but not the target */
5057 RemoveField(oldx, oldy);
5059 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5061 DrawLevelField(oldx, oldy);
5066 else if (element == EL_BLOCKED)
5068 Blocked2Moving(x, y, &oldx, &oldy);
5069 if (!IS_MOVING(oldx, oldy))
5073 if (element == EL_BLOCKED &&
5074 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5075 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5076 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5077 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5078 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5079 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5080 next_element = get_next_element(Feld[oldx][oldy]);
5082 RemoveField(oldx, oldy);
5083 RemoveField(newx, newy);
5085 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5087 if (next_element != EL_UNDEFINED)
5088 Feld[oldx][oldy] = next_element;
5090 DrawLevelField(oldx, oldy);
5091 DrawLevelField(newx, newy);
5094 void DrawDynamite(int x, int y)
5096 int sx = SCREENX(x), sy = SCREENY(y);
5097 int graphic = el2img(Feld[x][y]);
5100 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5103 if (IS_WALKABLE_INSIDE(Back[x][y]))
5107 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5108 else if (Store[x][y])
5109 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5111 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5113 if (Back[x][y] || Store[x][y])
5114 DrawGraphicThruMask(sx, sy, graphic, frame);
5116 DrawGraphic(sx, sy, graphic, frame);
5119 void CheckDynamite(int x, int y)
5121 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5125 if (MovDelay[x][y] != 0)
5128 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5134 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5139 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5141 boolean num_checked_players = 0;
5144 for (i = 0; i < MAX_PLAYERS; i++)
5146 if (stored_player[i].active)
5148 int sx = stored_player[i].jx;
5149 int sy = stored_player[i].jy;
5151 if (num_checked_players == 0)
5158 *sx1 = MIN(*sx1, sx);
5159 *sy1 = MIN(*sy1, sy);
5160 *sx2 = MAX(*sx2, sx);
5161 *sy2 = MAX(*sy2, sy);
5164 num_checked_players++;
5169 static boolean checkIfAllPlayersFitToScreen_RND()
5171 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5173 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5175 return (sx2 - sx1 < SCR_FIELDX &&
5176 sy2 - sy1 < SCR_FIELDY);
5179 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5181 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5183 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5185 *sx = (sx1 + sx2) / 2;
5186 *sy = (sy1 + sy2) / 2;
5189 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5190 boolean center_screen, boolean quick_relocation)
5192 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5193 boolean no_delay = (tape.warp_forward);
5194 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5195 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5197 if (quick_relocation)
5199 int offset = game.scroll_delay_value;
5201 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5203 if (!level.shifted_relocation || center_screen)
5205 /* quick relocation (without scrolling), with centering of screen */
5207 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5208 x > SBX_Right + MIDPOSX ? SBX_Right :
5211 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5212 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5217 /* quick relocation (without scrolling), but do not center screen */
5219 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5220 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5223 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5224 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5227 int offset_x = x + (scroll_x - center_scroll_x);
5228 int offset_y = y + (scroll_y - center_scroll_y);
5230 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5231 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5232 offset_x - MIDPOSX);
5234 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5235 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5236 offset_y - MIDPOSY);
5241 /* quick relocation (without scrolling), inside visible screen area */
5243 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
5244 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5245 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5247 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
5248 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5249 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5251 /* don't scroll over playfield boundaries */
5252 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5253 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5255 /* don't scroll over playfield boundaries */
5256 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5257 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5260 RedrawPlayfield(TRUE, 0,0,0,0);
5265 int scroll_xx, scroll_yy;
5267 if (!level.shifted_relocation || center_screen)
5269 /* visible relocation (with scrolling), with centering of screen */
5271 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5272 x > SBX_Right + MIDPOSX ? SBX_Right :
5275 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5276 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5281 /* visible relocation (with scrolling), but do not center screen */
5283 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5284 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5287 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5288 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5291 int offset_x = x + (scroll_x - center_scroll_x);
5292 int offset_y = y + (scroll_y - center_scroll_y);
5294 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5295 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5296 offset_x - MIDPOSX);
5298 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5299 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5300 offset_y - MIDPOSY);
5305 /* visible relocation (with scrolling), with centering of screen */
5307 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5308 x > SBX_Right + MIDPOSX ? SBX_Right :
5311 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5312 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5316 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5318 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5321 int fx = FX, fy = FY;
5323 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5324 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5326 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5332 fx += dx * TILEX / 2;
5333 fy += dy * TILEY / 2;
5335 ScrollLevel(dx, dy);
5338 /* scroll in two steps of half tile size to make things smoother */
5339 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5341 Delay(wait_delay_value);
5343 /* scroll second step to align at full tile size */
5345 Delay(wait_delay_value);
5350 Delay(wait_delay_value);
5354 void RelocatePlayer(int jx, int jy, int el_player_raw)
5356 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5357 int player_nr = GET_PLAYER_NR(el_player);
5358 struct PlayerInfo *player = &stored_player[player_nr];
5359 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5360 boolean no_delay = (tape.warp_forward);
5361 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5362 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5363 int old_jx = player->jx;
5364 int old_jy = player->jy;
5365 int old_element = Feld[old_jx][old_jy];
5366 int element = Feld[jx][jy];
5367 boolean player_relocated = (old_jx != jx || old_jy != jy);
5369 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5370 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5371 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5372 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5373 int leave_side_horiz = move_dir_horiz;
5374 int leave_side_vert = move_dir_vert;
5375 int enter_side = enter_side_horiz | enter_side_vert;
5376 int leave_side = leave_side_horiz | leave_side_vert;
5378 if (player->GameOver) /* do not reanimate dead player */
5381 if (!player_relocated) /* no need to relocate the player */
5384 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5386 RemoveField(jx, jy); /* temporarily remove newly placed player */
5387 DrawLevelField(jx, jy);
5390 if (player->present)
5392 while (player->MovPos)
5394 ScrollPlayer(player, SCROLL_GO_ON);
5395 ScrollScreen(NULL, SCROLL_GO_ON);
5397 AdvanceFrameAndPlayerCounters(player->index_nr);
5402 Delay(wait_delay_value);
5405 DrawPlayer(player); /* needed here only to cleanup last field */
5406 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5408 player->is_moving = FALSE;
5411 if (IS_CUSTOM_ELEMENT(old_element))
5412 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5414 player->index_bit, leave_side);
5416 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5418 player->index_bit, leave_side);
5420 Feld[jx][jy] = el_player;
5421 InitPlayerField(jx, jy, el_player, TRUE);
5423 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5425 Feld[jx][jy] = element;
5426 InitField(jx, jy, FALSE);
5429 /* only visually relocate centered player */
5430 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5431 FALSE, level.instant_relocation);
5433 TestIfPlayerTouchesBadThing(jx, jy);
5434 TestIfPlayerTouchesCustomElement(jx, jy);
5436 if (IS_CUSTOM_ELEMENT(element))
5437 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5438 player->index_bit, enter_side);
5440 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5441 player->index_bit, enter_side);
5444 void Explode(int ex, int ey, int phase, int mode)
5450 /* !!! eliminate this variable !!! */
5451 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5453 if (game.explosions_delayed)
5455 ExplodeField[ex][ey] = mode;
5459 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5461 int center_element = Feld[ex][ey];
5462 int artwork_element, explosion_element; /* set these values later */
5465 /* --- This is only really needed (and now handled) in "Impact()". --- */
5466 /* do not explode moving elements that left the explode field in time */
5467 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5468 center_element == EL_EMPTY &&
5469 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5474 /* !!! at this place, the center element may be EL_BLOCKED !!! */
5475 if (mode == EX_TYPE_NORMAL ||
5476 mode == EX_TYPE_CENTER ||
5477 mode == EX_TYPE_CROSS)
5478 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5481 /* remove things displayed in background while burning dynamite */
5482 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5485 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5487 /* put moving element to center field (and let it explode there) */
5488 center_element = MovingOrBlocked2Element(ex, ey);
5489 RemoveMovingField(ex, ey);
5490 Feld[ex][ey] = center_element;
5493 /* now "center_element" is finally determined -- set related values now */
5494 artwork_element = center_element; /* for custom player artwork */
5495 explosion_element = center_element; /* for custom player artwork */
5497 if (IS_PLAYER(ex, ey))
5499 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5501 artwork_element = stored_player[player_nr].artwork_element;
5503 if (level.use_explosion_element[player_nr])
5505 explosion_element = level.explosion_element[player_nr];
5506 artwork_element = explosion_element;
5511 if (mode == EX_TYPE_NORMAL ||
5512 mode == EX_TYPE_CENTER ||
5513 mode == EX_TYPE_CROSS)
5514 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5517 last_phase = element_info[explosion_element].explosion_delay + 1;
5519 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5521 int xx = x - ex + 1;
5522 int yy = y - ey + 1;
5525 if (!IN_LEV_FIELD(x, y) ||
5526 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5527 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5530 element = Feld[x][y];
5532 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5534 element = MovingOrBlocked2Element(x, y);
5536 if (!IS_EXPLOSION_PROOF(element))
5537 RemoveMovingField(x, y);
5540 /* indestructible elements can only explode in center (but not flames) */
5541 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5542 mode == EX_TYPE_BORDER)) ||
5543 element == EL_FLAMES)
5546 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5547 behaviour, for example when touching a yamyam that explodes to rocks
5548 with active deadly shield, a rock is created under the player !!! */
5549 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5551 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5552 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5553 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5555 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5558 if (IS_ACTIVE_BOMB(element))
5560 /* re-activate things under the bomb like gate or penguin */
5561 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5568 /* save walkable background elements while explosion on same tile */
5569 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5570 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5571 Back[x][y] = element;
5573 /* ignite explodable elements reached by other explosion */
5574 if (element == EL_EXPLOSION)
5575 element = Store2[x][y];
5577 if (AmoebaNr[x][y] &&
5578 (element == EL_AMOEBA_FULL ||
5579 element == EL_BD_AMOEBA ||
5580 element == EL_AMOEBA_GROWING))
5582 AmoebaCnt[AmoebaNr[x][y]]--;
5583 AmoebaCnt2[AmoebaNr[x][y]]--;
5588 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5590 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5592 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5594 if (PLAYERINFO(ex, ey)->use_murphy)
5595 Store[x][y] = EL_EMPTY;
5598 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5599 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5600 else if (ELEM_IS_PLAYER(center_element))
5601 Store[x][y] = EL_EMPTY;
5602 else if (center_element == EL_YAMYAM)
5603 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5604 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5605 Store[x][y] = element_info[center_element].content.e[xx][yy];
5607 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5608 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5609 otherwise) -- FIX THIS !!! */
5610 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5611 Store[x][y] = element_info[element].content.e[1][1];
5613 else if (!CAN_EXPLODE(element))
5614 Store[x][y] = element_info[element].content.e[1][1];
5617 Store[x][y] = EL_EMPTY;
5619 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5620 center_element == EL_AMOEBA_TO_DIAMOND)
5621 Store2[x][y] = element;
5623 Feld[x][y] = EL_EXPLOSION;
5624 GfxElement[x][y] = artwork_element;
5626 ExplodePhase[x][y] = 1;
5627 ExplodeDelay[x][y] = last_phase;
5632 if (center_element == EL_YAMYAM)
5633 game.yamyam_content_nr =
5634 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5646 GfxFrame[x][y] = 0; /* restart explosion animation */
5648 last_phase = ExplodeDelay[x][y];
5650 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5654 /* activate this even in non-DEBUG version until cause for crash in
5655 getGraphicAnimationFrame() (see below) is found and eliminated */
5661 /* this can happen if the player leaves an explosion just in time */
5662 if (GfxElement[x][y] == EL_UNDEFINED)
5663 GfxElement[x][y] = EL_EMPTY;
5665 if (GfxElement[x][y] == EL_UNDEFINED)
5668 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5669 printf("Explode(): This should never happen!\n");
5672 GfxElement[x][y] = EL_EMPTY;
5678 border_element = Store2[x][y];
5679 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5680 border_element = StorePlayer[x][y];
5682 if (phase == element_info[border_element].ignition_delay ||
5683 phase == last_phase)
5685 boolean border_explosion = FALSE;
5687 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5688 !PLAYER_EXPLOSION_PROTECTED(x, y))
5690 KillPlayerUnlessExplosionProtected(x, y);
5691 border_explosion = TRUE;
5693 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5695 Feld[x][y] = Store2[x][y];
5698 border_explosion = TRUE;
5700 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5702 AmoebeUmwandeln(x, y);
5704 border_explosion = TRUE;
5707 /* if an element just explodes due to another explosion (chain-reaction),
5708 do not immediately end the new explosion when it was the last frame of
5709 the explosion (as it would be done in the following "if"-statement!) */
5710 if (border_explosion && phase == last_phase)
5714 if (phase == last_phase)
5718 element = Feld[x][y] = Store[x][y];
5719 Store[x][y] = Store2[x][y] = 0;
5720 GfxElement[x][y] = EL_UNDEFINED;
5722 /* player can escape from explosions and might therefore be still alive */
5723 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5724 element <= EL_PLAYER_IS_EXPLODING_4)
5726 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5727 int explosion_element = EL_PLAYER_1 + player_nr;
5728 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5729 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5731 if (level.use_explosion_element[player_nr])
5732 explosion_element = level.explosion_element[player_nr];
5734 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5735 element_info[explosion_element].content.e[xx][yy]);
5738 /* restore probably existing indestructible background element */
5739 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5740 element = Feld[x][y] = Back[x][y];
5743 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5744 GfxDir[x][y] = MV_NONE;
5745 ChangeDelay[x][y] = 0;
5746 ChangePage[x][y] = -1;
5748 #if USE_NEW_CUSTOM_VALUE
5749 CustomValue[x][y] = 0;
5752 InitField_WithBug2(x, y, FALSE);
5754 DrawLevelField(x, y);
5756 TestIfElementTouchesCustomElement(x, y);
5758 if (GFX_CRUMBLED(element))
5759 DrawLevelFieldCrumbledSandNeighbours(x, y);
5761 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5762 StorePlayer[x][y] = 0;
5764 if (ELEM_IS_PLAYER(element))
5765 RelocatePlayer(x, y, element);
5767 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5769 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5770 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5773 DrawLevelFieldCrumbledSand(x, y);
5775 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5777 DrawLevelElement(x, y, Back[x][y]);
5778 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5780 else if (IS_WALKABLE_UNDER(Back[x][y]))
5782 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5783 DrawLevelElementThruMask(x, y, Back[x][y]);
5785 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5786 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5790 void DynaExplode(int ex, int ey)
5793 int dynabomb_element = Feld[ex][ey];
5794 int dynabomb_size = 1;
5795 boolean dynabomb_xl = FALSE;
5796 struct PlayerInfo *player;
5797 static int xy[4][2] =
5805 if (IS_ACTIVE_BOMB(dynabomb_element))
5807 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5808 dynabomb_size = player->dynabomb_size;
5809 dynabomb_xl = player->dynabomb_xl;
5810 player->dynabombs_left++;
5813 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5815 for (i = 0; i < NUM_DIRECTIONS; i++)
5817 for (j = 1; j <= dynabomb_size; j++)
5819 int x = ex + j * xy[i][0];
5820 int y = ey + j * xy[i][1];
5823 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5826 element = Feld[x][y];
5828 /* do not restart explosions of fields with active bombs */
5829 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5832 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5834 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5835 !IS_DIGGABLE(element) && !dynabomb_xl)
5841 void Bang(int x, int y)
5843 int element = MovingOrBlocked2Element(x, y);
5844 int explosion_type = EX_TYPE_NORMAL;
5846 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5848 struct PlayerInfo *player = PLAYERINFO(x, y);
5850 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
5851 player->element_nr);
5853 if (level.use_explosion_element[player->index_nr])
5855 int explosion_element = level.explosion_element[player->index_nr];
5857 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5858 explosion_type = EX_TYPE_CROSS;
5859 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5860 explosion_type = EX_TYPE_CENTER;
5868 case EL_BD_BUTTERFLY:
5871 case EL_DARK_YAMYAM:
5875 RaiseScoreElement(element);
5878 case EL_DYNABOMB_PLAYER_1_ACTIVE:
5879 case EL_DYNABOMB_PLAYER_2_ACTIVE:
5880 case EL_DYNABOMB_PLAYER_3_ACTIVE:
5881 case EL_DYNABOMB_PLAYER_4_ACTIVE:
5882 case EL_DYNABOMB_INCREASE_NUMBER:
5883 case EL_DYNABOMB_INCREASE_SIZE:
5884 case EL_DYNABOMB_INCREASE_POWER:
5885 explosion_type = EX_TYPE_DYNA;
5888 case EL_DC_LANDMINE:
5890 case EL_EM_EXIT_OPEN:
5891 case EL_EM_STEEL_EXIT_OPEN:
5893 explosion_type = EX_TYPE_CENTER;
5898 case EL_LAMP_ACTIVE:
5899 case EL_AMOEBA_TO_DIAMOND:
5900 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
5901 explosion_type = EX_TYPE_CENTER;
5905 if (element_info[element].explosion_type == EXPLODES_CROSS)
5906 explosion_type = EX_TYPE_CROSS;
5907 else if (element_info[element].explosion_type == EXPLODES_1X1)
5908 explosion_type = EX_TYPE_CENTER;
5912 if (explosion_type == EX_TYPE_DYNA)
5915 Explode(x, y, EX_PHASE_START, explosion_type);
5917 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5920 void SplashAcid(int x, int y)
5922 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5923 (!IN_LEV_FIELD(x - 1, y - 2) ||
5924 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5925 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5927 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5928 (!IN_LEV_FIELD(x + 1, y - 2) ||
5929 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5930 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5932 PlayLevelSound(x, y, SND_ACID_SPLASHING);
5935 static void InitBeltMovement()
5937 static int belt_base_element[4] =
5939 EL_CONVEYOR_BELT_1_LEFT,
5940 EL_CONVEYOR_BELT_2_LEFT,
5941 EL_CONVEYOR_BELT_3_LEFT,
5942 EL_CONVEYOR_BELT_4_LEFT
5944 static int belt_base_active_element[4] =
5946 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5947 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5948 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5949 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5954 /* set frame order for belt animation graphic according to belt direction */
5955 for (i = 0; i < NUM_BELTS; i++)
5959 for (j = 0; j < NUM_BELT_PARTS; j++)
5961 int element = belt_base_active_element[belt_nr] + j;
5962 int graphic_1 = el2img(element);
5963 int graphic_2 = el2panelimg(element);
5965 if (game.belt_dir[i] == MV_LEFT)
5967 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5968 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5972 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5973 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5978 SCAN_PLAYFIELD(x, y)
5980 int element = Feld[x][y];
5982 for (i = 0; i < NUM_BELTS; i++)
5984 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5986 int e_belt_nr = getBeltNrFromBeltElement(element);
5989 if (e_belt_nr == belt_nr)
5991 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5993 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6000 static void ToggleBeltSwitch(int x, int y)
6002 static int belt_base_element[4] =
6004 EL_CONVEYOR_BELT_1_LEFT,
6005 EL_CONVEYOR_BELT_2_LEFT,
6006 EL_CONVEYOR_BELT_3_LEFT,
6007 EL_CONVEYOR_BELT_4_LEFT
6009 static int belt_base_active_element[4] =
6011 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6012 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6013 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6014 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6016 static int belt_base_switch_element[4] =
6018 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6019 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6020 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6021 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6023 static int belt_move_dir[4] =
6031 int element = Feld[x][y];
6032 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6033 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6034 int belt_dir = belt_move_dir[belt_dir_nr];
6037 if (!IS_BELT_SWITCH(element))
6040 game.belt_dir_nr[belt_nr] = belt_dir_nr;
6041 game.belt_dir[belt_nr] = belt_dir;
6043 if (belt_dir_nr == 3)
6046 /* set frame order for belt animation graphic according to belt direction */
6047 for (i = 0; i < NUM_BELT_PARTS; i++)
6049 int element = belt_base_active_element[belt_nr] + i;
6050 int graphic_1 = el2img(element);
6051 int graphic_2 = el2panelimg(element);
6053 if (belt_dir == MV_LEFT)
6055 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6056 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6060 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6061 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6065 SCAN_PLAYFIELD(xx, yy)
6067 int element = Feld[xx][yy];
6069 if (IS_BELT_SWITCH(element))
6071 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6073 if (e_belt_nr == belt_nr)
6075 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6076 DrawLevelField(xx, yy);
6079 else if (IS_BELT(element) && belt_dir != MV_NONE)
6081 int e_belt_nr = getBeltNrFromBeltElement(element);
6083 if (e_belt_nr == belt_nr)
6085 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6087 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6088 DrawLevelField(xx, yy);
6091 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6093 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6095 if (e_belt_nr == belt_nr)
6097 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6099 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6100 DrawLevelField(xx, yy);
6106 static void ToggleSwitchgateSwitch(int x, int y)
6110 game.switchgate_pos = !game.switchgate_pos;
6112 SCAN_PLAYFIELD(xx, yy)
6114 int element = Feld[xx][yy];
6116 #if !USE_BOTH_SWITCHGATE_SWITCHES
6117 if (element == EL_SWITCHGATE_SWITCH_UP ||
6118 element == EL_SWITCHGATE_SWITCH_DOWN)
6120 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6121 DrawLevelField(xx, yy);
6123 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6124 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6126 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6127 DrawLevelField(xx, yy);
6130 if (element == EL_SWITCHGATE_SWITCH_UP)
6132 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6133 DrawLevelField(xx, yy);
6135 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6137 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6138 DrawLevelField(xx, yy);
6140 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6142 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6143 DrawLevelField(xx, yy);
6145 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6147 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6148 DrawLevelField(xx, yy);
6151 else if (element == EL_SWITCHGATE_OPEN ||
6152 element == EL_SWITCHGATE_OPENING)
6154 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6156 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6158 else if (element == EL_SWITCHGATE_CLOSED ||
6159 element == EL_SWITCHGATE_CLOSING)
6161 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6163 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6168 static int getInvisibleActiveFromInvisibleElement(int element)
6170 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6171 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6172 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6176 static int getInvisibleFromInvisibleActiveElement(int element)
6178 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6179 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6180 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6184 static void RedrawAllLightSwitchesAndInvisibleElements()
6188 SCAN_PLAYFIELD(x, y)
6190 int element = Feld[x][y];
6192 if (element == EL_LIGHT_SWITCH &&
6193 game.light_time_left > 0)
6195 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6196 DrawLevelField(x, y);
6198 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6199 game.light_time_left == 0)
6201 Feld[x][y] = EL_LIGHT_SWITCH;
6202 DrawLevelField(x, y);
6204 else if (element == EL_EMC_DRIPPER &&
6205 game.light_time_left > 0)
6207 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6208 DrawLevelField(x, y);
6210 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6211 game.light_time_left == 0)
6213 Feld[x][y] = EL_EMC_DRIPPER;
6214 DrawLevelField(x, y);
6216 else if (element == EL_INVISIBLE_STEELWALL ||
6217 element == EL_INVISIBLE_WALL ||
6218 element == EL_INVISIBLE_SAND)
6220 if (game.light_time_left > 0)
6221 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6223 DrawLevelField(x, y);
6225 /* uncrumble neighbour fields, if needed */
6226 if (element == EL_INVISIBLE_SAND)
6227 DrawLevelFieldCrumbledSandNeighbours(x, y);
6229 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6230 element == EL_INVISIBLE_WALL_ACTIVE ||
6231 element == EL_INVISIBLE_SAND_ACTIVE)
6233 if (game.light_time_left == 0)
6234 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6236 DrawLevelField(x, y);
6238 /* re-crumble neighbour fields, if needed */
6239 if (element == EL_INVISIBLE_SAND)
6240 DrawLevelFieldCrumbledSandNeighbours(x, y);
6245 static void RedrawAllInvisibleElementsForLenses()
6249 SCAN_PLAYFIELD(x, y)
6251 int element = Feld[x][y];
6253 if (element == EL_EMC_DRIPPER &&
6254 game.lenses_time_left > 0)
6256 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6257 DrawLevelField(x, y);
6259 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6260 game.lenses_time_left == 0)
6262 Feld[x][y] = EL_EMC_DRIPPER;
6263 DrawLevelField(x, y);
6265 else if (element == EL_INVISIBLE_STEELWALL ||
6266 element == EL_INVISIBLE_WALL ||
6267 element == EL_INVISIBLE_SAND)
6269 if (game.lenses_time_left > 0)
6270 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6272 DrawLevelField(x, y);
6274 /* uncrumble neighbour fields, if needed */
6275 if (element == EL_INVISIBLE_SAND)
6276 DrawLevelFieldCrumbledSandNeighbours(x, y);
6278 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6279 element == EL_INVISIBLE_WALL_ACTIVE ||
6280 element == EL_INVISIBLE_SAND_ACTIVE)
6282 if (game.lenses_time_left == 0)
6283 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6285 DrawLevelField(x, y);
6287 /* re-crumble neighbour fields, if needed */
6288 if (element == EL_INVISIBLE_SAND)
6289 DrawLevelFieldCrumbledSandNeighbours(x, y);
6294 static void RedrawAllInvisibleElementsForMagnifier()
6298 SCAN_PLAYFIELD(x, y)
6300 int element = Feld[x][y];
6302 if (element == EL_EMC_FAKE_GRASS &&
6303 game.magnify_time_left > 0)
6305 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6306 DrawLevelField(x, y);
6308 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6309 game.magnify_time_left == 0)
6311 Feld[x][y] = EL_EMC_FAKE_GRASS;
6312 DrawLevelField(x, y);
6314 else if (IS_GATE_GRAY(element) &&
6315 game.magnify_time_left > 0)
6317 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6318 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6319 IS_EM_GATE_GRAY(element) ?
6320 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6321 IS_EMC_GATE_GRAY(element) ?
6322 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6324 DrawLevelField(x, y);
6326 else if (IS_GATE_GRAY_ACTIVE(element) &&
6327 game.magnify_time_left == 0)
6329 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6330 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6331 IS_EM_GATE_GRAY_ACTIVE(element) ?
6332 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6333 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6334 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6336 DrawLevelField(x, y);
6341 static void ToggleLightSwitch(int x, int y)
6343 int element = Feld[x][y];
6345 game.light_time_left =
6346 (element == EL_LIGHT_SWITCH ?
6347 level.time_light * FRAMES_PER_SECOND : 0);
6349 RedrawAllLightSwitchesAndInvisibleElements();
6352 static void ActivateTimegateSwitch(int x, int y)
6356 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6358 SCAN_PLAYFIELD(xx, yy)
6360 int element = Feld[xx][yy];
6362 if (element == EL_TIMEGATE_CLOSED ||
6363 element == EL_TIMEGATE_CLOSING)
6365 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6366 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6370 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6372 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6373 DrawLevelField(xx, yy);
6380 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6381 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6383 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6387 void Impact(int x, int y)
6389 boolean last_line = (y == lev_fieldy - 1);
6390 boolean object_hit = FALSE;
6391 boolean impact = (last_line || object_hit);
6392 int element = Feld[x][y];
6393 int smashed = EL_STEELWALL;
6395 if (!last_line) /* check if element below was hit */
6397 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6400 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6401 MovDir[x][y + 1] != MV_DOWN ||
6402 MovPos[x][y + 1] <= TILEY / 2));
6404 /* do not smash moving elements that left the smashed field in time */
6405 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6406 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6409 #if USE_QUICKSAND_IMPACT_BUGFIX
6410 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6412 RemoveMovingField(x, y + 1);
6413 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6414 Feld[x][y + 2] = EL_ROCK;
6415 DrawLevelField(x, y + 2);
6420 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6422 RemoveMovingField(x, y + 1);
6423 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6424 Feld[x][y + 2] = EL_ROCK;
6425 DrawLevelField(x, y + 2);
6432 smashed = MovingOrBlocked2Element(x, y + 1);
6434 impact = (last_line || object_hit);
6437 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6439 SplashAcid(x, y + 1);
6443 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6444 /* only reset graphic animation if graphic really changes after impact */
6446 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6448 ResetGfxAnimation(x, y);
6449 DrawLevelField(x, y);
6452 if (impact && CAN_EXPLODE_IMPACT(element))
6457 else if (impact && element == EL_PEARL &&
6458 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6460 ResetGfxAnimation(x, y);
6462 Feld[x][y] = EL_PEARL_BREAKING;
6463 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6466 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6468 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6473 if (impact && element == EL_AMOEBA_DROP)
6475 if (object_hit && IS_PLAYER(x, y + 1))
6476 KillPlayerUnlessEnemyProtected(x, y + 1);
6477 else if (object_hit && smashed == EL_PENGUIN)
6481 Feld[x][y] = EL_AMOEBA_GROWING;
6482 Store[x][y] = EL_AMOEBA_WET;
6484 ResetRandomAnimationValue(x, y);
6489 if (object_hit) /* check which object was hit */
6491 if ((CAN_PASS_MAGIC_WALL(element) &&
6492 (smashed == EL_MAGIC_WALL ||
6493 smashed == EL_BD_MAGIC_WALL)) ||
6494 (CAN_PASS_DC_MAGIC_WALL(element) &&
6495 smashed == EL_DC_MAGIC_WALL))
6498 int activated_magic_wall =
6499 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6500 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6501 EL_DC_MAGIC_WALL_ACTIVE);
6503 /* activate magic wall / mill */
6504 SCAN_PLAYFIELD(xx, yy)
6506 if (Feld[xx][yy] == smashed)
6507 Feld[xx][yy] = activated_magic_wall;
6510 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6511 game.magic_wall_active = TRUE;
6513 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6514 SND_MAGIC_WALL_ACTIVATING :
6515 smashed == EL_BD_MAGIC_WALL ?
6516 SND_BD_MAGIC_WALL_ACTIVATING :
6517 SND_DC_MAGIC_WALL_ACTIVATING));
6520 if (IS_PLAYER(x, y + 1))
6522 if (CAN_SMASH_PLAYER(element))
6524 KillPlayerUnlessEnemyProtected(x, y + 1);
6528 else if (smashed == EL_PENGUIN)
6530 if (CAN_SMASH_PLAYER(element))
6536 else if (element == EL_BD_DIAMOND)
6538 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6544 else if (((element == EL_SP_INFOTRON ||
6545 element == EL_SP_ZONK) &&
6546 (smashed == EL_SP_SNIKSNAK ||
6547 smashed == EL_SP_ELECTRON ||
6548 smashed == EL_SP_DISK_ORANGE)) ||
6549 (element == EL_SP_INFOTRON &&
6550 smashed == EL_SP_DISK_YELLOW))
6555 else if (CAN_SMASH_EVERYTHING(element))
6557 if (IS_CLASSIC_ENEMY(smashed) ||
6558 CAN_EXPLODE_SMASHED(smashed))
6563 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6565 if (smashed == EL_LAMP ||
6566 smashed == EL_LAMP_ACTIVE)
6571 else if (smashed == EL_NUT)
6573 Feld[x][y + 1] = EL_NUT_BREAKING;
6574 PlayLevelSound(x, y, SND_NUT_BREAKING);
6575 RaiseScoreElement(EL_NUT);
6578 else if (smashed == EL_PEARL)
6580 ResetGfxAnimation(x, y);
6582 Feld[x][y + 1] = EL_PEARL_BREAKING;
6583 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6586 else if (smashed == EL_DIAMOND)
6588 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6589 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6592 else if (IS_BELT_SWITCH(smashed))
6594 ToggleBeltSwitch(x, y + 1);
6596 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6597 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6598 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6599 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6601 ToggleSwitchgateSwitch(x, y + 1);
6603 else if (smashed == EL_LIGHT_SWITCH ||
6604 smashed == EL_LIGHT_SWITCH_ACTIVE)
6606 ToggleLightSwitch(x, y + 1);
6611 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6614 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6616 CheckElementChangeBySide(x, y + 1, smashed, element,
6617 CE_SWITCHED, CH_SIDE_TOP);
6618 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6624 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6629 /* play sound of magic wall / mill */
6631 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6632 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6633 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6635 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6636 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6637 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6638 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6639 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6640 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6645 /* play sound of object that hits the ground */
6646 if (last_line || object_hit)
6647 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6650 inline static void TurnRoundExt(int x, int y)
6662 { 0, 0 }, { 0, 0 }, { 0, 0 },
6667 int left, right, back;
6671 { MV_DOWN, MV_UP, MV_RIGHT },
6672 { MV_UP, MV_DOWN, MV_LEFT },
6674 { MV_LEFT, MV_RIGHT, MV_DOWN },
6678 { MV_RIGHT, MV_LEFT, MV_UP }
6681 int element = Feld[x][y];
6682 int move_pattern = element_info[element].move_pattern;
6684 int old_move_dir = MovDir[x][y];
6685 int left_dir = turn[old_move_dir].left;
6686 int right_dir = turn[old_move_dir].right;
6687 int back_dir = turn[old_move_dir].back;
6689 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6690 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6691 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6692 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6694 int left_x = x + left_dx, left_y = y + left_dy;
6695 int right_x = x + right_dx, right_y = y + right_dy;
6696 int move_x = x + move_dx, move_y = y + move_dy;
6700 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6702 TestIfBadThingTouchesOtherBadThing(x, y);
6704 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6705 MovDir[x][y] = right_dir;
6706 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6707 MovDir[x][y] = left_dir;
6709 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6711 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6714 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6716 TestIfBadThingTouchesOtherBadThing(x, y);
6718 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6719 MovDir[x][y] = left_dir;
6720 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6721 MovDir[x][y] = right_dir;
6723 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6725 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6728 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6730 TestIfBadThingTouchesOtherBadThing(x, y);
6732 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6733 MovDir[x][y] = left_dir;
6734 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6735 MovDir[x][y] = right_dir;
6737 if (MovDir[x][y] != old_move_dir)
6740 else if (element == EL_YAMYAM)
6742 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6743 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6745 if (can_turn_left && can_turn_right)
6746 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6747 else if (can_turn_left)
6748 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6749 else if (can_turn_right)
6750 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6752 MovDir[x][y] = back_dir;
6754 MovDelay[x][y] = 16 + 16 * RND(3);
6756 else if (element == EL_DARK_YAMYAM)
6758 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6760 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6763 if (can_turn_left && can_turn_right)
6764 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6765 else if (can_turn_left)
6766 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6767 else if (can_turn_right)
6768 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6770 MovDir[x][y] = back_dir;
6772 MovDelay[x][y] = 16 + 16 * RND(3);
6774 else if (element == EL_PACMAN)
6776 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6777 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6779 if (can_turn_left && can_turn_right)
6780 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6781 else if (can_turn_left)
6782 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6783 else if (can_turn_right)
6784 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6786 MovDir[x][y] = back_dir;
6788 MovDelay[x][y] = 6 + RND(40);
6790 else if (element == EL_PIG)
6792 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6793 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6794 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6795 boolean should_turn_left, should_turn_right, should_move_on;
6797 int rnd = RND(rnd_value);
6799 should_turn_left = (can_turn_left &&
6801 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6802 y + back_dy + left_dy)));
6803 should_turn_right = (can_turn_right &&
6805 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6806 y + back_dy + right_dy)));
6807 should_move_on = (can_move_on &&
6810 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6811 y + move_dy + left_dy) ||
6812 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6813 y + move_dy + right_dy)));
6815 if (should_turn_left || should_turn_right || should_move_on)
6817 if (should_turn_left && should_turn_right && should_move_on)
6818 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6819 rnd < 2 * rnd_value / 3 ? right_dir :
6821 else if (should_turn_left && should_turn_right)
6822 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6823 else if (should_turn_left && should_move_on)
6824 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6825 else if (should_turn_right && should_move_on)
6826 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6827 else if (should_turn_left)
6828 MovDir[x][y] = left_dir;
6829 else if (should_turn_right)
6830 MovDir[x][y] = right_dir;
6831 else if (should_move_on)
6832 MovDir[x][y] = old_move_dir;
6834 else if (can_move_on && rnd > rnd_value / 8)
6835 MovDir[x][y] = old_move_dir;
6836 else if (can_turn_left && can_turn_right)
6837 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6838 else if (can_turn_left && rnd > rnd_value / 8)
6839 MovDir[x][y] = left_dir;
6840 else if (can_turn_right && rnd > rnd_value/8)
6841 MovDir[x][y] = right_dir;
6843 MovDir[x][y] = back_dir;
6845 xx = x + move_xy[MovDir[x][y]].dx;
6846 yy = y + move_xy[MovDir[x][y]].dy;
6848 if (!IN_LEV_FIELD(xx, yy) ||
6849 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6850 MovDir[x][y] = old_move_dir;
6854 else if (element == EL_DRAGON)
6856 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6857 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6858 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6860 int rnd = RND(rnd_value);
6862 if (can_move_on && rnd > rnd_value / 8)
6863 MovDir[x][y] = old_move_dir;
6864 else if (can_turn_left && can_turn_right)
6865 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6866 else if (can_turn_left && rnd > rnd_value / 8)
6867 MovDir[x][y] = left_dir;
6868 else if (can_turn_right && rnd > rnd_value / 8)
6869 MovDir[x][y] = right_dir;
6871 MovDir[x][y] = back_dir;
6873 xx = x + move_xy[MovDir[x][y]].dx;
6874 yy = y + move_xy[MovDir[x][y]].dy;
6876 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6877 MovDir[x][y] = old_move_dir;
6881 else if (element == EL_MOLE)
6883 boolean can_move_on =
6884 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6885 IS_AMOEBOID(Feld[move_x][move_y]) ||
6886 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6889 boolean can_turn_left =
6890 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6891 IS_AMOEBOID(Feld[left_x][left_y])));
6893 boolean can_turn_right =
6894 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6895 IS_AMOEBOID(Feld[right_x][right_y])));
6897 if (can_turn_left && can_turn_right)
6898 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6899 else if (can_turn_left)
6900 MovDir[x][y] = left_dir;
6902 MovDir[x][y] = right_dir;
6905 if (MovDir[x][y] != old_move_dir)
6908 else if (element == EL_BALLOON)
6910 MovDir[x][y] = game.wind_direction;
6913 else if (element == EL_SPRING)
6915 #if USE_NEW_SPRING_BUMPER
6916 if (MovDir[x][y] & MV_HORIZONTAL)
6918 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6919 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6921 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6922 ResetGfxAnimation(move_x, move_y);
6923 DrawLevelField(move_x, move_y);
6925 MovDir[x][y] = back_dir;
6927 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6928 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6929 MovDir[x][y] = MV_NONE;
6932 if (MovDir[x][y] & MV_HORIZONTAL &&
6933 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6934 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
6935 MovDir[x][y] = MV_NONE;
6940 else if (element == EL_ROBOT ||
6941 element == EL_SATELLITE ||
6942 element == EL_PENGUIN ||
6943 element == EL_EMC_ANDROID)
6945 int attr_x = -1, attr_y = -1;
6956 for (i = 0; i < MAX_PLAYERS; i++)
6958 struct PlayerInfo *player = &stored_player[i];
6959 int jx = player->jx, jy = player->jy;
6961 if (!player->active)
6965 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6973 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6974 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6975 game.engine_version < VERSION_IDENT(3,1,0,0)))
6981 if (element == EL_PENGUIN)
6984 static int xy[4][2] =
6992 for (i = 0; i < NUM_DIRECTIONS; i++)
6994 int ex = x + xy[i][0];
6995 int ey = y + xy[i][1];
6997 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6998 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6999 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7000 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7009 MovDir[x][y] = MV_NONE;
7011 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7012 else if (attr_x > x)
7013 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7015 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7016 else if (attr_y > y)
7017 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7019 if (element == EL_ROBOT)
7023 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7024 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7025 Moving2Blocked(x, y, &newx, &newy);
7027 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7028 MovDelay[x][y] = 8 + 8 * !RND(3);
7030 MovDelay[x][y] = 16;
7032 else if (element == EL_PENGUIN)
7038 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7040 boolean first_horiz = RND(2);
7041 int new_move_dir = MovDir[x][y];
7044 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7045 Moving2Blocked(x, y, &newx, &newy);
7047 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7051 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7052 Moving2Blocked(x, y, &newx, &newy);
7054 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7057 MovDir[x][y] = old_move_dir;
7061 else if (element == EL_SATELLITE)
7067 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7069 boolean first_horiz = RND(2);
7070 int new_move_dir = MovDir[x][y];
7073 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7074 Moving2Blocked(x, y, &newx, &newy);
7076 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7080 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7081 Moving2Blocked(x, y, &newx, &newy);
7083 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7086 MovDir[x][y] = old_move_dir;
7090 else if (element == EL_EMC_ANDROID)
7092 static int check_pos[16] =
7094 -1, /* 0 => (invalid) */
7095 7, /* 1 => MV_LEFT */
7096 3, /* 2 => MV_RIGHT */
7097 -1, /* 3 => (invalid) */
7099 0, /* 5 => MV_LEFT | MV_UP */
7100 2, /* 6 => MV_RIGHT | MV_UP */
7101 -1, /* 7 => (invalid) */
7102 5, /* 8 => MV_DOWN */
7103 6, /* 9 => MV_LEFT | MV_DOWN */
7104 4, /* 10 => MV_RIGHT | MV_DOWN */
7105 -1, /* 11 => (invalid) */
7106 -1, /* 12 => (invalid) */
7107 -1, /* 13 => (invalid) */
7108 -1, /* 14 => (invalid) */
7109 -1, /* 15 => (invalid) */
7117 { -1, -1, MV_LEFT | MV_UP },
7119 { +1, -1, MV_RIGHT | MV_UP },
7120 { +1, 0, MV_RIGHT },
7121 { +1, +1, MV_RIGHT | MV_DOWN },
7123 { -1, +1, MV_LEFT | MV_DOWN },
7126 int start_pos, check_order;
7127 boolean can_clone = FALSE;
7130 /* check if there is any free field around current position */
7131 for (i = 0; i < 8; i++)
7133 int newx = x + check_xy[i].dx;
7134 int newy = y + check_xy[i].dy;
7136 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7144 if (can_clone) /* randomly find an element to clone */
7148 start_pos = check_pos[RND(8)];
7149 check_order = (RND(2) ? -1 : +1);
7151 for (i = 0; i < 8; i++)
7153 int pos_raw = start_pos + i * check_order;
7154 int pos = (pos_raw + 8) % 8;
7155 int newx = x + check_xy[pos].dx;
7156 int newy = y + check_xy[pos].dy;
7158 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7160 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7161 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7163 Store[x][y] = Feld[newx][newy];
7172 if (can_clone) /* randomly find a direction to move */
7176 start_pos = check_pos[RND(8)];
7177 check_order = (RND(2) ? -1 : +1);
7179 for (i = 0; i < 8; i++)
7181 int pos_raw = start_pos + i * check_order;
7182 int pos = (pos_raw + 8) % 8;
7183 int newx = x + check_xy[pos].dx;
7184 int newy = y + check_xy[pos].dy;
7185 int new_move_dir = check_xy[pos].dir;
7187 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7189 MovDir[x][y] = new_move_dir;
7190 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7199 if (can_clone) /* cloning and moving successful */
7202 /* cannot clone -- try to move towards player */
7204 start_pos = check_pos[MovDir[x][y] & 0x0f];
7205 check_order = (RND(2) ? -1 : +1);
7207 for (i = 0; i < 3; i++)
7209 /* first check start_pos, then previous/next or (next/previous) pos */
7210 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7211 int pos = (pos_raw + 8) % 8;
7212 int newx = x + check_xy[pos].dx;
7213 int newy = y + check_xy[pos].dy;
7214 int new_move_dir = check_xy[pos].dir;
7216 if (IS_PLAYER(newx, newy))
7219 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7221 MovDir[x][y] = new_move_dir;
7222 MovDelay[x][y] = level.android_move_time * 8 + 1;
7229 else if (move_pattern == MV_TURNING_LEFT ||
7230 move_pattern == MV_TURNING_RIGHT ||
7231 move_pattern == MV_TURNING_LEFT_RIGHT ||
7232 move_pattern == MV_TURNING_RIGHT_LEFT ||
7233 move_pattern == MV_TURNING_RANDOM ||
7234 move_pattern == MV_ALL_DIRECTIONS)
7236 boolean can_turn_left =
7237 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7238 boolean can_turn_right =
7239 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7241 if (element_info[element].move_stepsize == 0) /* "not moving" */
7244 if (move_pattern == MV_TURNING_LEFT)
7245 MovDir[x][y] = left_dir;
7246 else if (move_pattern == MV_TURNING_RIGHT)
7247 MovDir[x][y] = right_dir;
7248 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7249 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7250 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7251 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7252 else if (move_pattern == MV_TURNING_RANDOM)
7253 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7254 can_turn_right && !can_turn_left ? right_dir :
7255 RND(2) ? left_dir : right_dir);
7256 else if (can_turn_left && can_turn_right)
7257 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7258 else if (can_turn_left)
7259 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7260 else if (can_turn_right)
7261 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7263 MovDir[x][y] = back_dir;
7265 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7267 else if (move_pattern == MV_HORIZONTAL ||
7268 move_pattern == MV_VERTICAL)
7270 if (move_pattern & old_move_dir)
7271 MovDir[x][y] = back_dir;
7272 else if (move_pattern == MV_HORIZONTAL)
7273 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7274 else if (move_pattern == MV_VERTICAL)
7275 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7277 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7279 else if (move_pattern & MV_ANY_DIRECTION)
7281 MovDir[x][y] = move_pattern;
7282 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7284 else if (move_pattern & MV_WIND_DIRECTION)
7286 MovDir[x][y] = game.wind_direction;
7287 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7289 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7291 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7292 MovDir[x][y] = left_dir;
7293 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7294 MovDir[x][y] = right_dir;
7296 if (MovDir[x][y] != old_move_dir)
7297 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7299 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7301 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7302 MovDir[x][y] = right_dir;
7303 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7304 MovDir[x][y] = left_dir;
7306 if (MovDir[x][y] != old_move_dir)
7307 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7309 else if (move_pattern == MV_TOWARDS_PLAYER ||
7310 move_pattern == MV_AWAY_FROM_PLAYER)
7312 int attr_x = -1, attr_y = -1;
7314 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7325 for (i = 0; i < MAX_PLAYERS; i++)
7327 struct PlayerInfo *player = &stored_player[i];
7328 int jx = player->jx, jy = player->jy;
7330 if (!player->active)
7334 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7342 MovDir[x][y] = MV_NONE;
7344 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7345 else if (attr_x > x)
7346 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7348 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7349 else if (attr_y > y)
7350 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7352 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7354 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7356 boolean first_horiz = RND(2);
7357 int new_move_dir = MovDir[x][y];
7359 if (element_info[element].move_stepsize == 0) /* "not moving" */
7361 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7362 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7368 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7369 Moving2Blocked(x, y, &newx, &newy);
7371 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7375 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7376 Moving2Blocked(x, y, &newx, &newy);
7378 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7381 MovDir[x][y] = old_move_dir;
7384 else if (move_pattern == MV_WHEN_PUSHED ||
7385 move_pattern == MV_WHEN_DROPPED)
7387 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7388 MovDir[x][y] = MV_NONE;
7392 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7394 static int test_xy[7][2] =
7404 static int test_dir[7] =
7414 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7415 int move_preference = -1000000; /* start with very low preference */
7416 int new_move_dir = MV_NONE;
7417 int start_test = RND(4);
7420 for (i = 0; i < NUM_DIRECTIONS; i++)
7422 int move_dir = test_dir[start_test + i];
7423 int move_dir_preference;
7425 xx = x + test_xy[start_test + i][0];
7426 yy = y + test_xy[start_test + i][1];
7428 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7429 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7431 new_move_dir = move_dir;
7436 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7439 move_dir_preference = -1 * RunnerVisit[xx][yy];
7440 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7441 move_dir_preference = PlayerVisit[xx][yy];
7443 if (move_dir_preference > move_preference)
7445 /* prefer field that has not been visited for the longest time */
7446 move_preference = move_dir_preference;
7447 new_move_dir = move_dir;
7449 else if (move_dir_preference == move_preference &&
7450 move_dir == old_move_dir)
7452 /* prefer last direction when all directions are preferred equally */
7453 move_preference = move_dir_preference;
7454 new_move_dir = move_dir;
7458 MovDir[x][y] = new_move_dir;
7459 if (old_move_dir != new_move_dir)
7460 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7464 static void TurnRound(int x, int y)
7466 int direction = MovDir[x][y];
7470 GfxDir[x][y] = MovDir[x][y];
7472 if (direction != MovDir[x][y])
7476 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7478 ResetGfxFrame(x, y, FALSE);
7481 static boolean JustBeingPushed(int x, int y)
7485 for (i = 0; i < MAX_PLAYERS; i++)
7487 struct PlayerInfo *player = &stored_player[i];
7489 if (player->active && player->is_pushing && player->MovPos)
7491 int next_jx = player->jx + (player->jx - player->last_jx);
7492 int next_jy = player->jy + (player->jy - player->last_jy);
7494 if (x == next_jx && y == next_jy)
7502 void StartMoving(int x, int y)
7504 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7505 int element = Feld[x][y];
7510 if (MovDelay[x][y] == 0)
7511 GfxAction[x][y] = ACTION_DEFAULT;
7513 if (CAN_FALL(element) && y < lev_fieldy - 1)
7515 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7516 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7517 if (JustBeingPushed(x, y))
7520 if (element == EL_QUICKSAND_FULL)
7522 if (IS_FREE(x, y + 1))
7524 InitMovingField(x, y, MV_DOWN);
7525 started_moving = TRUE;
7527 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7528 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7529 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7530 Store[x][y] = EL_ROCK;
7532 Store[x][y] = EL_ROCK;
7535 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7537 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7539 if (!MovDelay[x][y])
7540 MovDelay[x][y] = TILEY + 1;
7549 Feld[x][y] = EL_QUICKSAND_EMPTY;
7550 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7551 Store[x][y + 1] = Store[x][y];
7554 PlayLevelSoundAction(x, y, ACTION_FILLING);
7557 else if (element == EL_QUICKSAND_FAST_FULL)
7559 if (IS_FREE(x, y + 1))
7561 InitMovingField(x, y, MV_DOWN);
7562 started_moving = TRUE;
7564 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7565 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7566 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7567 Store[x][y] = EL_ROCK;
7569 Store[x][y] = EL_ROCK;
7572 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7574 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7576 if (!MovDelay[x][y])
7577 MovDelay[x][y] = TILEY + 1;
7586 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7587 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7588 Store[x][y + 1] = Store[x][y];
7591 PlayLevelSoundAction(x, y, ACTION_FILLING);
7594 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7595 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7597 InitMovingField(x, y, MV_DOWN);
7598 started_moving = TRUE;
7600 Feld[x][y] = EL_QUICKSAND_FILLING;
7601 Store[x][y] = element;
7603 PlayLevelSoundAction(x, y, ACTION_FILLING);
7605 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7606 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7608 InitMovingField(x, y, MV_DOWN);
7609 started_moving = TRUE;
7611 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7612 Store[x][y] = element;
7614 PlayLevelSoundAction(x, y, ACTION_FILLING);
7616 else if (element == EL_MAGIC_WALL_FULL)
7618 if (IS_FREE(x, y + 1))
7620 InitMovingField(x, y, MV_DOWN);
7621 started_moving = TRUE;
7623 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7624 Store[x][y] = EL_CHANGED(Store[x][y]);
7626 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7628 if (!MovDelay[x][y])
7629 MovDelay[x][y] = TILEY/4 + 1;
7638 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7639 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7640 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7644 else if (element == EL_BD_MAGIC_WALL_FULL)
7646 if (IS_FREE(x, y + 1))
7648 InitMovingField(x, y, MV_DOWN);
7649 started_moving = TRUE;
7651 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7652 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7654 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7656 if (!MovDelay[x][y])
7657 MovDelay[x][y] = TILEY/4 + 1;
7666 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7667 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7668 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7672 else if (element == EL_DC_MAGIC_WALL_FULL)
7674 if (IS_FREE(x, y + 1))
7676 InitMovingField(x, y, MV_DOWN);
7677 started_moving = TRUE;
7679 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7680 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7682 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7684 if (!MovDelay[x][y])
7685 MovDelay[x][y] = TILEY/4 + 1;
7694 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7695 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7696 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7700 else if ((CAN_PASS_MAGIC_WALL(element) &&
7701 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7702 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7703 (CAN_PASS_DC_MAGIC_WALL(element) &&
7704 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7707 InitMovingField(x, y, MV_DOWN);
7708 started_moving = TRUE;
7711 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7712 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7713 EL_DC_MAGIC_WALL_FILLING);
7714 Store[x][y] = element;
7716 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7718 SplashAcid(x, y + 1);
7720 InitMovingField(x, y, MV_DOWN);
7721 started_moving = TRUE;
7723 Store[x][y] = EL_ACID;
7726 #if USE_FIX_IMPACT_COLLISION
7727 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7728 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7730 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7731 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7733 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7734 CAN_FALL(element) && WasJustFalling[x][y] &&
7735 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7737 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7738 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7739 (Feld[x][y + 1] == EL_BLOCKED)))
7741 /* this is needed for a special case not covered by calling "Impact()"
7742 from "ContinueMoving()": if an element moves to a tile directly below
7743 another element which was just falling on that tile (which was empty
7744 in the previous frame), the falling element above would just stop
7745 instead of smashing the element below (in previous version, the above
7746 element was just checked for "moving" instead of "falling", resulting
7747 in incorrect smashes caused by horizontal movement of the above
7748 element; also, the case of the player being the element to smash was
7749 simply not covered here... :-/ ) */
7751 CheckCollision[x][y] = 0;
7752 CheckImpact[x][y] = 0;
7756 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7758 if (MovDir[x][y] == MV_NONE)
7760 InitMovingField(x, y, MV_DOWN);
7761 started_moving = TRUE;
7764 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7766 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7767 MovDir[x][y] = MV_DOWN;
7769 InitMovingField(x, y, MV_DOWN);
7770 started_moving = TRUE;
7772 else if (element == EL_AMOEBA_DROP)
7774 Feld[x][y] = EL_AMOEBA_GROWING;
7775 Store[x][y] = EL_AMOEBA_WET;
7777 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7778 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7779 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7780 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7782 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
7783 (IS_FREE(x - 1, y + 1) ||
7784 Feld[x - 1][y + 1] == EL_ACID));
7785 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7786 (IS_FREE(x + 1, y + 1) ||
7787 Feld[x + 1][y + 1] == EL_ACID));
7788 boolean can_fall_any = (can_fall_left || can_fall_right);
7789 boolean can_fall_both = (can_fall_left && can_fall_right);
7790 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7792 #if USE_NEW_ALL_SLIPPERY
7793 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7795 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7796 can_fall_right = FALSE;
7797 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7798 can_fall_left = FALSE;
7799 else if (slippery_type == SLIPPERY_ONLY_LEFT)
7800 can_fall_right = FALSE;
7801 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7802 can_fall_left = FALSE;
7804 can_fall_any = (can_fall_left || can_fall_right);
7805 can_fall_both = FALSE;
7808 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
7810 if (slippery_type == SLIPPERY_ONLY_LEFT)
7811 can_fall_right = FALSE;
7812 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7813 can_fall_left = FALSE;
7814 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7815 can_fall_right = FALSE;
7816 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7817 can_fall_left = FALSE;
7819 can_fall_any = (can_fall_left || can_fall_right);
7820 can_fall_both = (can_fall_left && can_fall_right);
7824 #if USE_NEW_ALL_SLIPPERY
7826 #if USE_NEW_SP_SLIPPERY
7827 /* !!! better use the same properties as for custom elements here !!! */
7828 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
7829 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
7831 can_fall_right = FALSE; /* slip down on left side */
7832 can_fall_both = FALSE;
7837 #if USE_NEW_ALL_SLIPPERY
7840 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7841 can_fall_right = FALSE; /* slip down on left side */
7843 can_fall_left = !(can_fall_right = RND(2));
7845 can_fall_both = FALSE;
7850 if (game.emulation == EMU_BOULDERDASH ||
7851 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7852 can_fall_right = FALSE; /* slip down on left side */
7854 can_fall_left = !(can_fall_right = RND(2));
7856 can_fall_both = FALSE;
7862 /* if not determined otherwise, prefer left side for slipping down */
7863 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7864 started_moving = TRUE;
7868 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
7870 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7873 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
7874 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7875 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7876 int belt_dir = game.belt_dir[belt_nr];
7878 if ((belt_dir == MV_LEFT && left_is_free) ||
7879 (belt_dir == MV_RIGHT && right_is_free))
7881 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7883 InitMovingField(x, y, belt_dir);
7884 started_moving = TRUE;
7886 Pushed[x][y] = TRUE;
7887 Pushed[nextx][y] = TRUE;
7889 GfxAction[x][y] = ACTION_DEFAULT;
7893 MovDir[x][y] = 0; /* if element was moving, stop it */
7898 /* not "else if" because of elements that can fall and move (EL_SPRING) */
7900 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
7902 if (CAN_MOVE(element) && !started_moving)
7905 int move_pattern = element_info[element].move_pattern;
7910 if (MovDir[x][y] == MV_NONE)
7912 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
7913 x, y, element, element_info[element].token_name);
7914 printf("StartMoving(): This should never happen!\n");
7919 Moving2Blocked(x, y, &newx, &newy);
7921 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7924 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7925 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7927 WasJustMoving[x][y] = 0;
7928 CheckCollision[x][y] = 0;
7930 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7932 if (Feld[x][y] != element) /* element has changed */
7936 if (!MovDelay[x][y]) /* start new movement phase */
7938 /* all objects that can change their move direction after each step
7939 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7941 if (element != EL_YAMYAM &&
7942 element != EL_DARK_YAMYAM &&
7943 element != EL_PACMAN &&
7944 !(move_pattern & MV_ANY_DIRECTION) &&
7945 move_pattern != MV_TURNING_LEFT &&
7946 move_pattern != MV_TURNING_RIGHT &&
7947 move_pattern != MV_TURNING_LEFT_RIGHT &&
7948 move_pattern != MV_TURNING_RIGHT_LEFT &&
7949 move_pattern != MV_TURNING_RANDOM)
7953 if (MovDelay[x][y] && (element == EL_BUG ||
7954 element == EL_SPACESHIP ||
7955 element == EL_SP_SNIKSNAK ||
7956 element == EL_SP_ELECTRON ||
7957 element == EL_MOLE))
7958 DrawLevelField(x, y);
7962 if (MovDelay[x][y]) /* wait some time before next movement */
7966 if (element == EL_ROBOT ||
7967 element == EL_YAMYAM ||
7968 element == EL_DARK_YAMYAM)
7970 DrawLevelElementAnimationIfNeeded(x, y, element);
7971 PlayLevelSoundAction(x, y, ACTION_WAITING);
7973 else if (element == EL_SP_ELECTRON)
7974 DrawLevelElementAnimationIfNeeded(x, y, element);
7975 else if (element == EL_DRAGON)
7978 int dir = MovDir[x][y];
7979 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7980 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
7981 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
7982 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
7983 dir == MV_UP ? IMG_FLAMES_1_UP :
7984 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7985 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7987 GfxAction[x][y] = ACTION_ATTACKING;
7989 if (IS_PLAYER(x, y))
7990 DrawPlayerField(x, y);
7992 DrawLevelField(x, y);
7994 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7996 for (i = 1; i <= 3; i++)
7998 int xx = x + i * dx;
7999 int yy = y + i * dy;
8000 int sx = SCREENX(xx);
8001 int sy = SCREENY(yy);
8002 int flame_graphic = graphic + (i - 1);
8004 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8009 int flamed = MovingOrBlocked2Element(xx, yy);
8013 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8015 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8016 RemoveMovingField(xx, yy);
8018 RemoveField(xx, yy);
8020 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8023 RemoveMovingField(xx, yy);
8026 ChangeDelay[xx][yy] = 0;
8028 Feld[xx][yy] = EL_FLAMES;
8030 if (IN_SCR_FIELD(sx, sy))
8032 DrawLevelFieldCrumbledSand(xx, yy);
8033 DrawGraphic(sx, sy, flame_graphic, frame);
8038 if (Feld[xx][yy] == EL_FLAMES)
8039 Feld[xx][yy] = EL_EMPTY;
8040 DrawLevelField(xx, yy);
8045 if (MovDelay[x][y]) /* element still has to wait some time */
8047 PlayLevelSoundAction(x, y, ACTION_WAITING);
8053 /* now make next step */
8055 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8057 if (DONT_COLLIDE_WITH(element) &&
8058 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8059 !PLAYER_ENEMY_PROTECTED(newx, newy))
8061 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8066 else if (CAN_MOVE_INTO_ACID(element) &&
8067 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8068 !IS_MV_DIAGONAL(MovDir[x][y]) &&
8069 (MovDir[x][y] == MV_DOWN ||
8070 game.engine_version >= VERSION_IDENT(3,1,0,0)))
8072 SplashAcid(newx, newy);
8073 Store[x][y] = EL_ACID;
8075 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8077 if (Feld[newx][newy] == EL_EXIT_OPEN ||
8078 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8079 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8080 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8083 DrawLevelField(x, y);
8085 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8086 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8087 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8089 local_player->friends_still_needed--;
8090 if (!local_player->friends_still_needed &&
8091 !local_player->GameOver && AllPlayersGone)
8092 PlayerWins(local_player);
8096 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8098 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8099 DrawLevelField(newx, newy);
8101 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8103 else if (!IS_FREE(newx, newy))
8105 GfxAction[x][y] = ACTION_WAITING;
8107 if (IS_PLAYER(x, y))
8108 DrawPlayerField(x, y);
8110 DrawLevelField(x, y);
8115 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8117 if (IS_FOOD_PIG(Feld[newx][newy]))
8119 if (IS_MOVING(newx, newy))
8120 RemoveMovingField(newx, newy);
8123 Feld[newx][newy] = EL_EMPTY;
8124 DrawLevelField(newx, newy);
8127 PlayLevelSound(x, y, SND_PIG_DIGGING);
8129 else if (!IS_FREE(newx, newy))
8131 if (IS_PLAYER(x, y))
8132 DrawPlayerField(x, y);
8134 DrawLevelField(x, y);
8139 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8141 if (Store[x][y] != EL_EMPTY)
8143 boolean can_clone = FALSE;
8146 /* check if element to clone is still there */
8147 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8149 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8157 /* cannot clone or target field not free anymore -- do not clone */
8158 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8159 Store[x][y] = EL_EMPTY;
8162 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8164 if (IS_MV_DIAGONAL(MovDir[x][y]))
8166 int diagonal_move_dir = MovDir[x][y];
8167 int stored = Store[x][y];
8168 int change_delay = 8;
8171 /* android is moving diagonally */
8173 CreateField(x, y, EL_DIAGONAL_SHRINKING);
8175 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8176 GfxElement[x][y] = EL_EMC_ANDROID;
8177 GfxAction[x][y] = ACTION_SHRINKING;
8178 GfxDir[x][y] = diagonal_move_dir;
8179 ChangeDelay[x][y] = change_delay;
8181 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8184 DrawLevelGraphicAnimation(x, y, graphic);
8185 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8187 if (Feld[newx][newy] == EL_ACID)
8189 SplashAcid(newx, newy);
8194 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8196 Store[newx][newy] = EL_EMC_ANDROID;
8197 GfxElement[newx][newy] = EL_EMC_ANDROID;
8198 GfxAction[newx][newy] = ACTION_GROWING;
8199 GfxDir[newx][newy] = diagonal_move_dir;
8200 ChangeDelay[newx][newy] = change_delay;
8202 graphic = el_act_dir2img(GfxElement[newx][newy],
8203 GfxAction[newx][newy], GfxDir[newx][newy]);
8205 DrawLevelGraphicAnimation(newx, newy, graphic);
8206 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8212 Feld[newx][newy] = EL_EMPTY;
8213 DrawLevelField(newx, newy);
8215 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8218 else if (!IS_FREE(newx, newy))
8221 if (IS_PLAYER(x, y))
8222 DrawPlayerField(x, y);
8224 DrawLevelField(x, y);
8230 else if (IS_CUSTOM_ELEMENT(element) &&
8231 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8233 int new_element = Feld[newx][newy];
8235 if (!IS_FREE(newx, newy))
8237 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8238 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8241 /* no element can dig solid indestructible elements */
8242 if (IS_INDESTRUCTIBLE(new_element) &&
8243 !IS_DIGGABLE(new_element) &&
8244 !IS_COLLECTIBLE(new_element))
8247 if (AmoebaNr[newx][newy] &&
8248 (new_element == EL_AMOEBA_FULL ||
8249 new_element == EL_BD_AMOEBA ||
8250 new_element == EL_AMOEBA_GROWING))
8252 AmoebaCnt[AmoebaNr[newx][newy]]--;
8253 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8256 if (IS_MOVING(newx, newy))
8257 RemoveMovingField(newx, newy);
8260 RemoveField(newx, newy);
8261 DrawLevelField(newx, newy);
8264 /* if digged element was about to explode, prevent the explosion */
8265 ExplodeField[newx][newy] = EX_TYPE_NONE;
8267 PlayLevelSoundAction(x, y, action);
8270 Store[newx][newy] = EL_EMPTY;
8272 /* this makes it possible to leave the removed element again */
8273 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8274 Store[newx][newy] = new_element;
8276 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8278 int move_leave_element = element_info[element].move_leave_element;
8280 /* this makes it possible to leave the removed element again */
8281 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8282 new_element : move_leave_element);
8286 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8288 RunnerVisit[x][y] = FrameCounter;
8289 PlayerVisit[x][y] /= 8; /* expire player visit path */
8292 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8294 if (!IS_FREE(newx, newy))
8296 if (IS_PLAYER(x, y))
8297 DrawPlayerField(x, y);
8299 DrawLevelField(x, y);
8305 boolean wanna_flame = !RND(10);
8306 int dx = newx - x, dy = newy - y;
8307 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8308 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8309 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8310 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8311 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8312 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8315 IS_CLASSIC_ENEMY(element1) ||
8316 IS_CLASSIC_ENEMY(element2)) &&
8317 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8318 element1 != EL_FLAMES && element2 != EL_FLAMES)
8320 ResetGfxAnimation(x, y);
8321 GfxAction[x][y] = ACTION_ATTACKING;
8323 if (IS_PLAYER(x, y))
8324 DrawPlayerField(x, y);
8326 DrawLevelField(x, y);
8328 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8330 MovDelay[x][y] = 50;
8334 RemoveField(newx, newy);
8336 Feld[newx][newy] = EL_FLAMES;
8337 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8340 RemoveField(newx1, newy1);
8342 Feld[newx1][newy1] = EL_FLAMES;
8344 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8347 RemoveField(newx2, newy2);
8349 Feld[newx2][newy2] = EL_FLAMES;
8356 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8357 Feld[newx][newy] == EL_DIAMOND)
8359 if (IS_MOVING(newx, newy))
8360 RemoveMovingField(newx, newy);
8363 Feld[newx][newy] = EL_EMPTY;
8364 DrawLevelField(newx, newy);
8367 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8369 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8370 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8372 if (AmoebaNr[newx][newy])
8374 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8375 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8376 Feld[newx][newy] == EL_BD_AMOEBA)
8377 AmoebaCnt[AmoebaNr[newx][newy]]--;
8382 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8384 RemoveMovingField(newx, newy);
8387 if (IS_MOVING(newx, newy))
8389 RemoveMovingField(newx, newy);
8394 Feld[newx][newy] = EL_EMPTY;
8395 DrawLevelField(newx, newy);
8398 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8400 else if ((element == EL_PACMAN || element == EL_MOLE)
8401 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8403 if (AmoebaNr[newx][newy])
8405 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8406 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8407 Feld[newx][newy] == EL_BD_AMOEBA)
8408 AmoebaCnt[AmoebaNr[newx][newy]]--;
8411 if (element == EL_MOLE)
8413 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8414 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8416 ResetGfxAnimation(x, y);
8417 GfxAction[x][y] = ACTION_DIGGING;
8418 DrawLevelField(x, y);
8420 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8422 return; /* wait for shrinking amoeba */
8424 else /* element == EL_PACMAN */
8426 Feld[newx][newy] = EL_EMPTY;
8427 DrawLevelField(newx, newy);
8428 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8431 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8432 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8433 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8435 /* wait for shrinking amoeba to completely disappear */
8438 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8440 /* object was running against a wall */
8445 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8446 if (move_pattern & MV_ANY_DIRECTION &&
8447 move_pattern == MovDir[x][y])
8449 int blocking_element =
8450 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8452 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8455 element = Feld[x][y]; /* element might have changed */
8459 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8460 DrawLevelElementAnimation(x, y, element);
8462 if (DONT_TOUCH(element))
8463 TestIfBadThingTouchesPlayer(x, y);
8468 InitMovingField(x, y, MovDir[x][y]);
8470 PlayLevelSoundAction(x, y, ACTION_MOVING);
8474 ContinueMoving(x, y);
8477 void ContinueMoving(int x, int y)
8479 int element = Feld[x][y];
8480 struct ElementInfo *ei = &element_info[element];
8481 int direction = MovDir[x][y];
8482 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8483 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8484 int newx = x + dx, newy = y + dy;
8485 int stored = Store[x][y];
8486 int stored_new = Store[newx][newy];
8487 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
8488 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8489 boolean last_line = (newy == lev_fieldy - 1);
8491 MovPos[x][y] += getElementMoveStepsize(x, y);
8493 if (pushed_by_player) /* special case: moving object pushed by player */
8494 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8496 if (ABS(MovPos[x][y]) < TILEX)
8499 int ee = Feld[x][y];
8500 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8501 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8503 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8504 x, y, ABS(MovPos[x][y]),
8506 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8509 DrawLevelField(x, y);
8511 return; /* element is still moving */
8514 /* element reached destination field */
8516 Feld[x][y] = EL_EMPTY;
8517 Feld[newx][newy] = element;
8518 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8520 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
8522 element = Feld[newx][newy] = EL_ACID;
8524 else if (element == EL_MOLE)
8526 Feld[x][y] = EL_SAND;
8528 DrawLevelFieldCrumbledSandNeighbours(x, y);
8530 else if (element == EL_QUICKSAND_FILLING)
8532 element = Feld[newx][newy] = get_next_element(element);
8533 Store[newx][newy] = Store[x][y];
8535 else if (element == EL_QUICKSAND_EMPTYING)
8537 Feld[x][y] = get_next_element(element);
8538 element = Feld[newx][newy] = Store[x][y];
8540 else if (element == EL_QUICKSAND_FAST_FILLING)
8542 element = Feld[newx][newy] = get_next_element(element);
8543 Store[newx][newy] = Store[x][y];
8545 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8547 Feld[x][y] = get_next_element(element);
8548 element = Feld[newx][newy] = Store[x][y];
8550 else if (element == EL_MAGIC_WALL_FILLING)
8552 element = Feld[newx][newy] = get_next_element(element);
8553 if (!game.magic_wall_active)
8554 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8555 Store[newx][newy] = Store[x][y];
8557 else if (element == EL_MAGIC_WALL_EMPTYING)
8559 Feld[x][y] = get_next_element(element);
8560 if (!game.magic_wall_active)
8561 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8562 element = Feld[newx][newy] = Store[x][y];
8564 #if USE_NEW_CUSTOM_VALUE
8565 InitField(newx, newy, FALSE);
8568 else if (element == EL_BD_MAGIC_WALL_FILLING)
8570 element = Feld[newx][newy] = get_next_element(element);
8571 if (!game.magic_wall_active)
8572 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8573 Store[newx][newy] = Store[x][y];
8575 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8577 Feld[x][y] = get_next_element(element);
8578 if (!game.magic_wall_active)
8579 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8580 element = Feld[newx][newy] = Store[x][y];
8582 #if USE_NEW_CUSTOM_VALUE
8583 InitField(newx, newy, FALSE);
8586 else if (element == EL_DC_MAGIC_WALL_FILLING)
8588 element = Feld[newx][newy] = get_next_element(element);
8589 if (!game.magic_wall_active)
8590 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8591 Store[newx][newy] = Store[x][y];
8593 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8595 Feld[x][y] = get_next_element(element);
8596 if (!game.magic_wall_active)
8597 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8598 element = Feld[newx][newy] = Store[x][y];
8600 #if USE_NEW_CUSTOM_VALUE
8601 InitField(newx, newy, FALSE);
8604 else if (element == EL_AMOEBA_DROPPING)
8606 Feld[x][y] = get_next_element(element);
8607 element = Feld[newx][newy] = Store[x][y];
8609 else if (element == EL_SOKOBAN_OBJECT)
8612 Feld[x][y] = Back[x][y];
8614 if (Back[newx][newy])
8615 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8617 Back[x][y] = Back[newx][newy] = 0;
8620 Store[x][y] = EL_EMPTY;
8625 MovDelay[newx][newy] = 0;
8627 if (CAN_CHANGE_OR_HAS_ACTION(element))
8629 /* copy element change control values to new field */
8630 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8631 ChangePage[newx][newy] = ChangePage[x][y];
8632 ChangeCount[newx][newy] = ChangeCount[x][y];
8633 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8636 #if USE_NEW_CUSTOM_VALUE
8637 CustomValue[newx][newy] = CustomValue[x][y];
8640 ChangeDelay[x][y] = 0;
8641 ChangePage[x][y] = -1;
8642 ChangeCount[x][y] = 0;
8643 ChangeEvent[x][y] = -1;
8645 #if USE_NEW_CUSTOM_VALUE
8646 CustomValue[x][y] = 0;
8649 /* copy animation control values to new field */
8650 GfxFrame[newx][newy] = GfxFrame[x][y];
8651 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
8652 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
8653 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
8655 Pushed[x][y] = Pushed[newx][newy] = FALSE;
8657 /* some elements can leave other elements behind after moving */
8659 if (ei->move_leave_element != EL_EMPTY &&
8660 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8661 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8663 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
8664 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8665 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8668 int move_leave_element = ei->move_leave_element;
8672 /* this makes it possible to leave the removed element again */
8673 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8674 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8676 /* this makes it possible to leave the removed element again */
8677 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8678 move_leave_element = stored;
8681 /* this makes it possible to leave the removed element again */
8682 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8683 ei->move_leave_element == EL_TRIGGER_ELEMENT)
8684 move_leave_element = stored;
8687 Feld[x][y] = move_leave_element;
8689 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8690 MovDir[x][y] = direction;
8692 InitField(x, y, FALSE);
8694 if (GFX_CRUMBLED(Feld[x][y]))
8695 DrawLevelFieldCrumbledSandNeighbours(x, y);
8697 if (ELEM_IS_PLAYER(move_leave_element))
8698 RelocatePlayer(x, y, move_leave_element);
8701 /* do this after checking for left-behind element */
8702 ResetGfxAnimation(x, y); /* reset animation values for old field */
8704 if (!CAN_MOVE(element) ||
8705 (CAN_FALL(element) && direction == MV_DOWN &&
8706 (element == EL_SPRING ||
8707 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8708 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8709 GfxDir[x][y] = MovDir[newx][newy] = 0;
8711 DrawLevelField(x, y);
8712 DrawLevelField(newx, newy);
8714 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8716 /* prevent pushed element from moving on in pushed direction */
8717 if (pushed_by_player && CAN_MOVE(element) &&
8718 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8719 !(element_info[element].move_pattern & direction))
8720 TurnRound(newx, newy);
8722 /* prevent elements on conveyor belt from moving on in last direction */
8723 if (pushed_by_conveyor && CAN_FALL(element) &&
8724 direction & MV_HORIZONTAL)
8725 MovDir[newx][newy] = 0;
8727 if (!pushed_by_player)
8729 int nextx = newx + dx, nexty = newy + dy;
8730 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8732 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8734 if (CAN_FALL(element) && direction == MV_DOWN)
8735 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8737 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8738 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8740 #if USE_FIX_IMPACT_COLLISION
8741 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8742 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8746 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8748 TestIfBadThingTouchesPlayer(newx, newy);
8749 TestIfBadThingTouchesFriend(newx, newy);
8751 if (!IS_CUSTOM_ELEMENT(element))
8752 TestIfBadThingTouchesOtherBadThing(newx, newy);
8754 else if (element == EL_PENGUIN)
8755 TestIfFriendTouchesBadThing(newx, newy);
8757 /* give the player one last chance (one more frame) to move away */
8758 if (CAN_FALL(element) && direction == MV_DOWN &&
8759 (last_line || (!IS_FREE(x, newy + 1) &&
8760 (!IS_PLAYER(x, newy + 1) ||
8761 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8764 if (pushed_by_player && !game.use_change_when_pushing_bug)
8766 int push_side = MV_DIR_OPPOSITE(direction);
8767 struct PlayerInfo *player = PLAYERINFO(x, y);
8769 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8770 player->index_bit, push_side);
8771 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8772 player->index_bit, push_side);
8775 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
8776 MovDelay[newx][newy] = 1;
8778 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8780 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
8783 if (ChangePage[newx][newy] != -1) /* delayed change */
8785 int page = ChangePage[newx][newy];
8786 struct ElementChangeInfo *change = &ei->change_page[page];
8788 ChangePage[newx][newy] = -1;
8790 if (change->can_change)
8792 if (ChangeElement(newx, newy, element, page))
8794 if (change->post_change_function)
8795 change->post_change_function(newx, newy);
8799 if (change->has_action)
8800 ExecuteCustomElementAction(newx, newy, element, page);
8804 TestIfElementHitsCustomElement(newx, newy, direction);
8805 TestIfPlayerTouchesCustomElement(newx, newy);
8806 TestIfElementTouchesCustomElement(newx, newy);
8808 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8809 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8810 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8811 MV_DIR_OPPOSITE(direction));
8814 int AmoebeNachbarNr(int ax, int ay)
8817 int element = Feld[ax][ay];
8819 static int xy[4][2] =
8827 for (i = 0; i < NUM_DIRECTIONS; i++)
8829 int x = ax + xy[i][0];
8830 int y = ay + xy[i][1];
8832 if (!IN_LEV_FIELD(x, y))
8835 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8836 group_nr = AmoebaNr[x][y];
8842 void AmoebenVereinigen(int ax, int ay)
8844 int i, x, y, xx, yy;
8845 int new_group_nr = AmoebaNr[ax][ay];
8846 static int xy[4][2] =
8854 if (new_group_nr == 0)
8857 for (i = 0; i < NUM_DIRECTIONS; i++)
8862 if (!IN_LEV_FIELD(x, y))
8865 if ((Feld[x][y] == EL_AMOEBA_FULL ||
8866 Feld[x][y] == EL_BD_AMOEBA ||
8867 Feld[x][y] == EL_AMOEBA_DEAD) &&
8868 AmoebaNr[x][y] != new_group_nr)
8870 int old_group_nr = AmoebaNr[x][y];
8872 if (old_group_nr == 0)
8875 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8876 AmoebaCnt[old_group_nr] = 0;
8877 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8878 AmoebaCnt2[old_group_nr] = 0;
8880 SCAN_PLAYFIELD(xx, yy)
8882 if (AmoebaNr[xx][yy] == old_group_nr)
8883 AmoebaNr[xx][yy] = new_group_nr;
8889 void AmoebeUmwandeln(int ax, int ay)
8893 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8895 int group_nr = AmoebaNr[ax][ay];
8900 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8901 printf("AmoebeUmwandeln(): This should never happen!\n");
8906 SCAN_PLAYFIELD(x, y)
8908 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8911 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8915 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8916 SND_AMOEBA_TURNING_TO_GEM :
8917 SND_AMOEBA_TURNING_TO_ROCK));
8922 static int xy[4][2] =
8930 for (i = 0; i < NUM_DIRECTIONS; i++)
8935 if (!IN_LEV_FIELD(x, y))
8938 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8940 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8941 SND_AMOEBA_TURNING_TO_GEM :
8942 SND_AMOEBA_TURNING_TO_ROCK));
8949 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8952 int group_nr = AmoebaNr[ax][ay];
8953 boolean done = FALSE;
8958 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8959 printf("AmoebeUmwandelnBD(): This should never happen!\n");
8964 SCAN_PLAYFIELD(x, y)
8966 if (AmoebaNr[x][y] == group_nr &&
8967 (Feld[x][y] == EL_AMOEBA_DEAD ||
8968 Feld[x][y] == EL_BD_AMOEBA ||
8969 Feld[x][y] == EL_AMOEBA_GROWING))
8972 Feld[x][y] = new_element;
8973 InitField(x, y, FALSE);
8974 DrawLevelField(x, y);
8980 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8981 SND_BD_AMOEBA_TURNING_TO_ROCK :
8982 SND_BD_AMOEBA_TURNING_TO_GEM));
8985 void AmoebeWaechst(int x, int y)
8987 static unsigned long sound_delay = 0;
8988 static unsigned long sound_delay_value = 0;
8990 if (!MovDelay[x][y]) /* start new growing cycle */
8994 if (DelayReached(&sound_delay, sound_delay_value))
8996 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8997 sound_delay_value = 30;
9001 if (MovDelay[x][y]) /* wait some time before growing bigger */
9004 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9006 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9007 6 - MovDelay[x][y]);
9009 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9012 if (!MovDelay[x][y])
9014 Feld[x][y] = Store[x][y];
9016 DrawLevelField(x, y);
9021 void AmoebaDisappearing(int x, int y)
9023 static unsigned long sound_delay = 0;
9024 static unsigned long sound_delay_value = 0;
9026 if (!MovDelay[x][y]) /* start new shrinking cycle */
9030 if (DelayReached(&sound_delay, sound_delay_value))
9031 sound_delay_value = 30;
9034 if (MovDelay[x][y]) /* wait some time before shrinking */
9037 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9039 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9040 6 - MovDelay[x][y]);
9042 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9045 if (!MovDelay[x][y])
9047 Feld[x][y] = EL_EMPTY;
9048 DrawLevelField(x, y);
9050 /* don't let mole enter this field in this cycle;
9051 (give priority to objects falling to this field from above) */
9057 void AmoebeAbleger(int ax, int ay)
9060 int element = Feld[ax][ay];
9061 int graphic = el2img(element);
9062 int newax = ax, neway = ay;
9063 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9064 static int xy[4][2] =
9072 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9074 Feld[ax][ay] = EL_AMOEBA_DEAD;
9075 DrawLevelField(ax, ay);
9079 if (IS_ANIMATED(graphic))
9080 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9082 if (!MovDelay[ax][ay]) /* start making new amoeba field */
9083 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9085 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
9088 if (MovDelay[ax][ay])
9092 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9095 int x = ax + xy[start][0];
9096 int y = ay + xy[start][1];
9098 if (!IN_LEV_FIELD(x, y))
9101 if (IS_FREE(x, y) ||
9102 CAN_GROW_INTO(Feld[x][y]) ||
9103 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9104 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9110 if (newax == ax && neway == ay)
9113 else /* normal or "filled" (BD style) amoeba */
9116 boolean waiting_for_player = FALSE;
9118 for (i = 0; i < NUM_DIRECTIONS; i++)
9120 int j = (start + i) % 4;
9121 int x = ax + xy[j][0];
9122 int y = ay + xy[j][1];
9124 if (!IN_LEV_FIELD(x, y))
9127 if (IS_FREE(x, y) ||
9128 CAN_GROW_INTO(Feld[x][y]) ||
9129 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9130 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9136 else if (IS_PLAYER(x, y))
9137 waiting_for_player = TRUE;
9140 if (newax == ax && neway == ay) /* amoeba cannot grow */
9142 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9144 Feld[ax][ay] = EL_AMOEBA_DEAD;
9145 DrawLevelField(ax, ay);
9146 AmoebaCnt[AmoebaNr[ax][ay]]--;
9148 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
9150 if (element == EL_AMOEBA_FULL)
9151 AmoebeUmwandeln(ax, ay);
9152 else if (element == EL_BD_AMOEBA)
9153 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9158 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9160 /* amoeba gets larger by growing in some direction */
9162 int new_group_nr = AmoebaNr[ax][ay];
9165 if (new_group_nr == 0)
9167 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9168 printf("AmoebeAbleger(): This should never happen!\n");
9173 AmoebaNr[newax][neway] = new_group_nr;
9174 AmoebaCnt[new_group_nr]++;
9175 AmoebaCnt2[new_group_nr]++;
9177 /* if amoeba touches other amoeba(s) after growing, unify them */
9178 AmoebenVereinigen(newax, neway);
9180 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9182 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9188 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9189 (neway == lev_fieldy - 1 && newax != ax))
9191 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
9192 Store[newax][neway] = element;
9194 else if (neway == ay || element == EL_EMC_DRIPPER)
9196 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
9198 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9202 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
9203 Feld[ax][ay] = EL_AMOEBA_DROPPING;
9204 Store[ax][ay] = EL_AMOEBA_DROP;
9205 ContinueMoving(ax, ay);
9209 DrawLevelField(newax, neway);
9212 void Life(int ax, int ay)
9216 int element = Feld[ax][ay];
9217 int graphic = el2img(element);
9218 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9220 boolean changed = FALSE;
9222 if (IS_ANIMATED(graphic))
9223 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9228 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
9229 MovDelay[ax][ay] = life_time;
9231 if (MovDelay[ax][ay]) /* wait some time before next cycle */
9234 if (MovDelay[ax][ay])
9238 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9240 int xx = ax+x1, yy = ay+y1;
9243 if (!IN_LEV_FIELD(xx, yy))
9246 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9248 int x = xx+x2, y = yy+y2;
9250 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9253 if (((Feld[x][y] == element ||
9254 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9256 (IS_FREE(x, y) && Stop[x][y]))
9260 if (xx == ax && yy == ay) /* field in the middle */
9262 if (nachbarn < life_parameter[0] ||
9263 nachbarn > life_parameter[1])
9265 Feld[xx][yy] = EL_EMPTY;
9267 DrawLevelField(xx, yy);
9268 Stop[xx][yy] = TRUE;
9272 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9273 { /* free border field */
9274 if (nachbarn >= life_parameter[2] &&
9275 nachbarn <= life_parameter[3])
9277 Feld[xx][yy] = element;
9278 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9280 DrawLevelField(xx, yy);
9281 Stop[xx][yy] = TRUE;
9288 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9289 SND_GAME_OF_LIFE_GROWING);
9292 static void InitRobotWheel(int x, int y)
9294 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9297 static void RunRobotWheel(int x, int y)
9299 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9302 static void StopRobotWheel(int x, int y)
9304 if (ZX == x && ZY == y)
9308 game.robot_wheel_active = FALSE;
9312 static void InitTimegateWheel(int x, int y)
9314 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9317 static void RunTimegateWheel(int x, int y)
9319 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9322 static void InitMagicBallDelay(int x, int y)
9325 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9327 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9331 static void ActivateMagicBall(int bx, int by)
9335 if (level.ball_random)
9337 int pos_border = RND(8); /* select one of the eight border elements */
9338 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9339 int xx = pos_content % 3;
9340 int yy = pos_content / 3;
9345 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9346 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9350 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9352 int xx = x - bx + 1;
9353 int yy = y - by + 1;
9355 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9356 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9360 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9363 void CheckExit(int x, int y)
9365 if (local_player->gems_still_needed > 0 ||
9366 local_player->sokobanfields_still_needed > 0 ||
9367 local_player->lights_still_needed > 0)
9369 int element = Feld[x][y];
9370 int graphic = el2img(element);
9372 if (IS_ANIMATED(graphic))
9373 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9378 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9381 Feld[x][y] = EL_EXIT_OPENING;
9383 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9386 void CheckExitEM(int x, int y)
9388 if (local_player->gems_still_needed > 0 ||
9389 local_player->sokobanfields_still_needed > 0 ||
9390 local_player->lights_still_needed > 0)
9392 int element = Feld[x][y];
9393 int graphic = el2img(element);
9395 if (IS_ANIMATED(graphic))
9396 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9401 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9404 Feld[x][y] = EL_EM_EXIT_OPENING;
9406 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9409 void CheckExitSteel(int x, int y)
9411 if (local_player->gems_still_needed > 0 ||
9412 local_player->sokobanfields_still_needed > 0 ||
9413 local_player->lights_still_needed > 0)
9415 int element = Feld[x][y];
9416 int graphic = el2img(element);
9418 if (IS_ANIMATED(graphic))
9419 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9424 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9427 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9429 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9432 void CheckExitSteelEM(int x, int y)
9434 if (local_player->gems_still_needed > 0 ||
9435 local_player->sokobanfields_still_needed > 0 ||
9436 local_player->lights_still_needed > 0)
9438 int element = Feld[x][y];
9439 int graphic = el2img(element);
9441 if (IS_ANIMATED(graphic))
9442 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9447 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9450 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9452 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9455 void CheckExitSP(int x, int y)
9457 if (local_player->gems_still_needed > 0)
9459 int element = Feld[x][y];
9460 int graphic = el2img(element);
9462 if (IS_ANIMATED(graphic))
9463 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9468 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9471 Feld[x][y] = EL_SP_EXIT_OPENING;
9473 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9476 static void CloseAllOpenTimegates()
9480 SCAN_PLAYFIELD(x, y)
9482 int element = Feld[x][y];
9484 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9486 Feld[x][y] = EL_TIMEGATE_CLOSING;
9488 PlayLevelSoundAction(x, y, ACTION_CLOSING);
9493 void DrawTwinkleOnField(int x, int y)
9495 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9498 if (Feld[x][y] == EL_BD_DIAMOND)
9501 if (MovDelay[x][y] == 0) /* next animation frame */
9502 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9504 if (MovDelay[x][y] != 0) /* wait some time before next frame */
9508 if (setup.direct_draw && MovDelay[x][y])
9509 SetDrawtoField(DRAW_BUFFERED);
9511 DrawLevelElementAnimation(x, y, Feld[x][y]);
9513 if (MovDelay[x][y] != 0)
9515 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9516 10 - MovDelay[x][y]);
9518 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9520 if (setup.direct_draw)
9524 dest_x = FX + SCREENX(x) * TILEX;
9525 dest_y = FY + SCREENY(y) * TILEY;
9527 BlitBitmap(drawto_field, window,
9528 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
9529 SetDrawtoField(DRAW_DIRECT);
9535 void MauerWaechst(int x, int y)
9539 if (!MovDelay[x][y]) /* next animation frame */
9540 MovDelay[x][y] = 3 * delay;
9542 if (MovDelay[x][y]) /* wait some time before next frame */
9546 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9548 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9549 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9551 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9554 if (!MovDelay[x][y])
9556 if (MovDir[x][y] == MV_LEFT)
9558 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9559 DrawLevelField(x - 1, y);
9561 else if (MovDir[x][y] == MV_RIGHT)
9563 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9564 DrawLevelField(x + 1, y);
9566 else if (MovDir[x][y] == MV_UP)
9568 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9569 DrawLevelField(x, y - 1);
9573 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9574 DrawLevelField(x, y + 1);
9577 Feld[x][y] = Store[x][y];
9579 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9580 DrawLevelField(x, y);
9585 void MauerAbleger(int ax, int ay)
9587 int element = Feld[ax][ay];
9588 int graphic = el2img(element);
9589 boolean oben_frei = FALSE, unten_frei = FALSE;
9590 boolean links_frei = FALSE, rechts_frei = FALSE;
9591 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9592 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9593 boolean new_wall = FALSE;
9595 if (IS_ANIMATED(graphic))
9596 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9598 if (!MovDelay[ax][ay]) /* start building new wall */
9599 MovDelay[ax][ay] = 6;
9601 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9604 if (MovDelay[ax][ay])
9608 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9610 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9612 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9614 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9617 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9618 element == EL_EXPANDABLE_WALL_ANY)
9622 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9623 Store[ax][ay-1] = element;
9624 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9625 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9626 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9627 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9632 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9633 Store[ax][ay+1] = element;
9634 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9635 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9636 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9637 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9642 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9643 element == EL_EXPANDABLE_WALL_ANY ||
9644 element == EL_EXPANDABLE_WALL ||
9645 element == EL_BD_EXPANDABLE_WALL)
9649 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9650 Store[ax-1][ay] = element;
9651 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9652 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9653 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9654 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9660 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9661 Store[ax+1][ay] = element;
9662 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9663 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9664 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9665 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9670 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9671 DrawLevelField(ax, ay);
9673 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9675 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9676 unten_massiv = TRUE;
9677 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9678 links_massiv = TRUE;
9679 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9680 rechts_massiv = TRUE;
9682 if (((oben_massiv && unten_massiv) ||
9683 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9684 element == EL_EXPANDABLE_WALL) &&
9685 ((links_massiv && rechts_massiv) ||
9686 element == EL_EXPANDABLE_WALL_VERTICAL))
9687 Feld[ax][ay] = EL_WALL;
9690 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9693 void MauerAblegerStahl(int ax, int ay)
9695 int element = Feld[ax][ay];
9696 int graphic = el2img(element);
9697 boolean oben_frei = FALSE, unten_frei = FALSE;
9698 boolean links_frei = FALSE, rechts_frei = FALSE;
9699 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9700 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9701 boolean new_wall = FALSE;
9703 if (IS_ANIMATED(graphic))
9704 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9706 if (!MovDelay[ax][ay]) /* start building new wall */
9707 MovDelay[ax][ay] = 6;
9709 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9712 if (MovDelay[ax][ay])
9716 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9718 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9720 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9722 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9725 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9726 element == EL_EXPANDABLE_STEELWALL_ANY)
9730 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9731 Store[ax][ay-1] = element;
9732 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9733 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9734 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9735 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9740 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9741 Store[ax][ay+1] = element;
9742 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9743 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9744 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9745 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9750 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9751 element == EL_EXPANDABLE_STEELWALL_ANY)
9755 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9756 Store[ax-1][ay] = element;
9757 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9758 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9759 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9760 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9766 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9767 Store[ax+1][ay] = element;
9768 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9769 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9770 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9771 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9776 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9778 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9779 unten_massiv = TRUE;
9780 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9781 links_massiv = TRUE;
9782 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9783 rechts_massiv = TRUE;
9785 if (((oben_massiv && unten_massiv) ||
9786 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9787 ((links_massiv && rechts_massiv) ||
9788 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9789 Feld[ax][ay] = EL_WALL;
9792 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9795 void CheckForDragon(int x, int y)
9798 boolean dragon_found = FALSE;
9799 static int xy[4][2] =
9807 for (i = 0; i < NUM_DIRECTIONS; i++)
9809 for (j = 0; j < 4; j++)
9811 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9813 if (IN_LEV_FIELD(xx, yy) &&
9814 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9816 if (Feld[xx][yy] == EL_DRAGON)
9817 dragon_found = TRUE;
9826 for (i = 0; i < NUM_DIRECTIONS; i++)
9828 for (j = 0; j < 3; j++)
9830 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9832 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9834 Feld[xx][yy] = EL_EMPTY;
9835 DrawLevelField(xx, yy);
9844 static void InitBuggyBase(int x, int y)
9846 int element = Feld[x][y];
9847 int activating_delay = FRAMES_PER_SECOND / 4;
9850 (element == EL_SP_BUGGY_BASE ?
9851 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9852 element == EL_SP_BUGGY_BASE_ACTIVATING ?
9854 element == EL_SP_BUGGY_BASE_ACTIVE ?
9855 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9858 static void WarnBuggyBase(int x, int y)
9861 static int xy[4][2] =
9869 for (i = 0; i < NUM_DIRECTIONS; i++)
9871 int xx = x + xy[i][0];
9872 int yy = y + xy[i][1];
9874 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9876 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9883 static void InitTrap(int x, int y)
9885 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9888 static void ActivateTrap(int x, int y)
9890 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9893 static void ChangeActiveTrap(int x, int y)
9895 int graphic = IMG_TRAP_ACTIVE;
9897 /* if new animation frame was drawn, correct crumbled sand border */
9898 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9899 DrawLevelFieldCrumbledSand(x, y);
9902 static int getSpecialActionElement(int element, int number, int base_element)
9904 return (element != EL_EMPTY ? element :
9905 number != -1 ? base_element + number - 1 :
9909 static int getModifiedActionNumber(int value_old, int operator, int operand,
9910 int value_min, int value_max)
9912 int value_new = (operator == CA_MODE_SET ? operand :
9913 operator == CA_MODE_ADD ? value_old + operand :
9914 operator == CA_MODE_SUBTRACT ? value_old - operand :
9915 operator == CA_MODE_MULTIPLY ? value_old * operand :
9916 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
9917 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
9920 return (value_new < value_min ? value_min :
9921 value_new > value_max ? value_max :
9925 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9927 struct ElementInfo *ei = &element_info[element];
9928 struct ElementChangeInfo *change = &ei->change_page[page];
9929 int target_element = change->target_element;
9930 int action_type = change->action_type;
9931 int action_mode = change->action_mode;
9932 int action_arg = change->action_arg;
9935 if (!change->has_action)
9938 /* ---------- determine action paramater values -------------------------- */
9940 int level_time_value =
9941 (level.time > 0 ? TimeLeft :
9944 int action_arg_element =
9945 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
9946 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9947 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
9950 int action_arg_direction =
9951 (action_arg >= CA_ARG_DIRECTION_LEFT &&
9952 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9953 action_arg == CA_ARG_DIRECTION_TRIGGER ?
9954 change->actual_trigger_side :
9955 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9956 MV_DIR_OPPOSITE(change->actual_trigger_side) :
9959 int action_arg_number_min =
9960 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9963 int action_arg_number_max =
9964 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9965 action_type == CA_SET_LEVEL_GEMS ? 999 :
9966 action_type == CA_SET_LEVEL_TIME ? 9999 :
9967 action_type == CA_SET_LEVEL_SCORE ? 99999 :
9968 action_type == CA_SET_CE_VALUE ? 9999 :
9969 action_type == CA_SET_CE_SCORE ? 9999 :
9972 int action_arg_number_reset =
9973 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9974 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9975 action_type == CA_SET_LEVEL_TIME ? level.time :
9976 action_type == CA_SET_LEVEL_SCORE ? 0 :
9977 #if USE_NEW_CUSTOM_VALUE
9978 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9980 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
9982 action_type == CA_SET_CE_SCORE ? 0 :
9985 int action_arg_number =
9986 (action_arg <= CA_ARG_MAX ? action_arg :
9987 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9988 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9989 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9990 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9991 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9992 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9993 #if USE_NEW_CUSTOM_VALUE
9994 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9996 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
9998 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9999 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10000 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10001 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10002 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10003 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10004 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10005 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10006 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10007 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
10008 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10011 int action_arg_number_old =
10012 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10013 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10014 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10015 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10016 action_type == CA_SET_CE_SCORE ? ei->collect_score :
10019 int action_arg_number_new =
10020 getModifiedActionNumber(action_arg_number_old,
10021 action_mode, action_arg_number,
10022 action_arg_number_min, action_arg_number_max);
10025 int trigger_player_bits = change->actual_trigger_player_bits;
10027 int trigger_player_bits =
10028 (change->actual_trigger_player >= EL_PLAYER_1 &&
10029 change->actual_trigger_player <= EL_PLAYER_4 ?
10030 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10034 int action_arg_player_bits =
10035 (action_arg >= CA_ARG_PLAYER_1 &&
10036 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10037 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10040 /* ---------- execute action -------------------------------------------- */
10042 switch (action_type)
10049 /* ---------- level actions ------------------------------------------- */
10051 case CA_RESTART_LEVEL:
10053 game.restart_level = TRUE;
10058 case CA_SHOW_ENVELOPE:
10060 int element = getSpecialActionElement(action_arg_element,
10061 action_arg_number, EL_ENVELOPE_1);
10063 if (IS_ENVELOPE(element))
10064 local_player->show_envelope = element;
10069 case CA_SET_LEVEL_TIME:
10071 if (level.time > 0) /* only modify limited time value */
10073 TimeLeft = action_arg_number_new;
10076 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10078 DisplayGameControlValues();
10080 DrawGameValue_Time(TimeLeft);
10083 if (!TimeLeft && setup.time_limit)
10084 for (i = 0; i < MAX_PLAYERS; i++)
10085 KillPlayer(&stored_player[i]);
10091 case CA_SET_LEVEL_SCORE:
10093 local_player->score = action_arg_number_new;
10096 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10098 DisplayGameControlValues();
10100 DrawGameValue_Score(local_player->score);
10106 case CA_SET_LEVEL_GEMS:
10108 local_player->gems_still_needed = action_arg_number_new;
10111 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10113 DisplayGameControlValues();
10115 DrawGameValue_Emeralds(local_player->gems_still_needed);
10121 #if !USE_PLAYER_GRAVITY
10122 case CA_SET_LEVEL_GRAVITY:
10124 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10125 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10126 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10132 case CA_SET_LEVEL_WIND:
10134 game.wind_direction = action_arg_direction;
10139 /* ---------- player actions ------------------------------------------ */
10141 case CA_MOVE_PLAYER:
10143 /* automatically move to the next field in specified direction */
10144 for (i = 0; i < MAX_PLAYERS; i++)
10145 if (trigger_player_bits & (1 << i))
10146 stored_player[i].programmed_action = action_arg_direction;
10151 case CA_EXIT_PLAYER:
10153 for (i = 0; i < MAX_PLAYERS; i++)
10154 if (action_arg_player_bits & (1 << i))
10155 PlayerWins(&stored_player[i]);
10160 case CA_KILL_PLAYER:
10162 for (i = 0; i < MAX_PLAYERS; i++)
10163 if (action_arg_player_bits & (1 << i))
10164 KillPlayer(&stored_player[i]);
10169 case CA_SET_PLAYER_KEYS:
10171 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10172 int element = getSpecialActionElement(action_arg_element,
10173 action_arg_number, EL_KEY_1);
10175 if (IS_KEY(element))
10177 for (i = 0; i < MAX_PLAYERS; i++)
10179 if (trigger_player_bits & (1 << i))
10181 stored_player[i].key[KEY_NR(element)] = key_state;
10183 DrawGameDoorValues();
10191 case CA_SET_PLAYER_SPEED:
10193 for (i = 0; i < MAX_PLAYERS; i++)
10195 if (trigger_player_bits & (1 << i))
10197 int move_stepsize = TILEX / stored_player[i].move_delay_value;
10199 if (action_arg == CA_ARG_SPEED_FASTER &&
10200 stored_player[i].cannot_move)
10202 action_arg_number = STEPSIZE_VERY_SLOW;
10204 else if (action_arg == CA_ARG_SPEED_SLOWER ||
10205 action_arg == CA_ARG_SPEED_FASTER)
10207 action_arg_number = 2;
10208 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10211 else if (action_arg == CA_ARG_NUMBER_RESET)
10213 action_arg_number = level.initial_player_stepsize[i];
10217 getModifiedActionNumber(move_stepsize,
10220 action_arg_number_min,
10221 action_arg_number_max);
10223 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10230 case CA_SET_PLAYER_SHIELD:
10232 for (i = 0; i < MAX_PLAYERS; i++)
10234 if (trigger_player_bits & (1 << i))
10236 if (action_arg == CA_ARG_SHIELD_OFF)
10238 stored_player[i].shield_normal_time_left = 0;
10239 stored_player[i].shield_deadly_time_left = 0;
10241 else if (action_arg == CA_ARG_SHIELD_NORMAL)
10243 stored_player[i].shield_normal_time_left = 999999;
10245 else if (action_arg == CA_ARG_SHIELD_DEADLY)
10247 stored_player[i].shield_normal_time_left = 999999;
10248 stored_player[i].shield_deadly_time_left = 999999;
10256 #if USE_PLAYER_GRAVITY
10257 case CA_SET_PLAYER_GRAVITY:
10259 for (i = 0; i < MAX_PLAYERS; i++)
10261 if (trigger_player_bits & (1 << i))
10263 stored_player[i].gravity =
10264 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10265 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10266 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10267 stored_player[i].gravity);
10275 case CA_SET_PLAYER_ARTWORK:
10277 for (i = 0; i < MAX_PLAYERS; i++)
10279 if (trigger_player_bits & (1 << i))
10281 int artwork_element = action_arg_element;
10283 if (action_arg == CA_ARG_ELEMENT_RESET)
10285 (level.use_artwork_element[i] ? level.artwork_element[i] :
10286 stored_player[i].element_nr);
10288 #if USE_GFX_RESET_PLAYER_ARTWORK
10289 if (stored_player[i].artwork_element != artwork_element)
10290 stored_player[i].Frame = 0;
10293 stored_player[i].artwork_element = artwork_element;
10295 SetPlayerWaiting(&stored_player[i], FALSE);
10297 /* set number of special actions for bored and sleeping animation */
10298 stored_player[i].num_special_action_bored =
10299 get_num_special_action(artwork_element,
10300 ACTION_BORING_1, ACTION_BORING_LAST);
10301 stored_player[i].num_special_action_sleeping =
10302 get_num_special_action(artwork_element,
10303 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10310 /* ---------- CE actions ---------------------------------------------- */
10312 case CA_SET_CE_VALUE:
10314 #if USE_NEW_CUSTOM_VALUE
10315 int last_ce_value = CustomValue[x][y];
10317 CustomValue[x][y] = action_arg_number_new;
10319 if (CustomValue[x][y] != last_ce_value)
10321 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10322 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10324 if (CustomValue[x][y] == 0)
10326 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10327 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10335 case CA_SET_CE_SCORE:
10337 #if USE_NEW_CUSTOM_VALUE
10338 int last_ce_score = ei->collect_score;
10340 ei->collect_score = action_arg_number_new;
10342 if (ei->collect_score != last_ce_score)
10344 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10345 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10347 if (ei->collect_score == 0)
10351 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10352 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10355 This is a very special case that seems to be a mixture between
10356 CheckElementChange() and CheckTriggeredElementChange(): while
10357 the first one only affects single elements that are triggered
10358 directly, the second one affects multiple elements in the playfield
10359 that are triggered indirectly by another element. This is a third
10360 case: Changing the CE score always affects multiple identical CEs,
10361 so every affected CE must be checked, not only the single CE for
10362 which the CE score was changed in the first place (as every instance
10363 of that CE shares the same CE score, and therefore also can change)!
10365 SCAN_PLAYFIELD(xx, yy)
10367 if (Feld[xx][yy] == element)
10368 CheckElementChange(xx, yy, element, EL_UNDEFINED,
10369 CE_SCORE_GETS_ZERO);
10378 /* ---------- engine actions ------------------------------------------ */
10380 case CA_SET_ENGINE_SCAN_MODE:
10382 InitPlayfieldScanMode(action_arg);
10392 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10394 int old_element = Feld[x][y];
10395 int new_element = GetElementFromGroupElement(element);
10396 int previous_move_direction = MovDir[x][y];
10397 #if USE_NEW_CUSTOM_VALUE
10398 int last_ce_value = CustomValue[x][y];
10400 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10401 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10402 boolean add_player_onto_element = (new_element_is_player &&
10403 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
10404 /* this breaks SnakeBite when a snake is
10405 halfway through a door that closes */
10406 /* NOW FIXED AT LEVEL INIT IN files.c */
10407 new_element != EL_SOKOBAN_FIELD_PLAYER &&
10409 IS_WALKABLE(old_element));
10412 /* check if element under the player changes from accessible to unaccessible
10413 (needed for special case of dropping element which then changes) */
10414 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10415 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10423 if (!add_player_onto_element)
10425 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10426 RemoveMovingField(x, y);
10430 Feld[x][y] = new_element;
10432 #if !USE_GFX_RESET_GFX_ANIMATION
10433 ResetGfxAnimation(x, y);
10434 ResetRandomAnimationValue(x, y);
10437 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10438 MovDir[x][y] = previous_move_direction;
10440 #if USE_NEW_CUSTOM_VALUE
10441 if (element_info[new_element].use_last_ce_value)
10442 CustomValue[x][y] = last_ce_value;
10445 InitField_WithBug1(x, y, FALSE);
10447 new_element = Feld[x][y]; /* element may have changed */
10449 #if USE_GFX_RESET_GFX_ANIMATION
10450 ResetGfxAnimation(x, y);
10451 ResetRandomAnimationValue(x, y);
10454 DrawLevelField(x, y);
10456 if (GFX_CRUMBLED(new_element))
10457 DrawLevelFieldCrumbledSandNeighbours(x, y);
10461 /* check if element under the player changes from accessible to unaccessible
10462 (needed for special case of dropping element which then changes) */
10463 /* (must be checked after creating new element for walkable group elements) */
10464 #if USE_FIX_KILLED_BY_NON_WALKABLE
10465 if (IS_PLAYER(x, y) && !player_explosion_protected &&
10466 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10473 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10474 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10483 /* "ChangeCount" not set yet to allow "entered by player" change one time */
10484 if (new_element_is_player)
10485 RelocatePlayer(x, y, new_element);
10488 ChangeCount[x][y]++; /* count number of changes in the same frame */
10490 TestIfBadThingTouchesPlayer(x, y);
10491 TestIfPlayerTouchesCustomElement(x, y);
10492 TestIfElementTouchesCustomElement(x, y);
10495 static void CreateField(int x, int y, int element)
10497 CreateFieldExt(x, y, element, FALSE);
10500 static void CreateElementFromChange(int x, int y, int element)
10502 element = GET_VALID_RUNTIME_ELEMENT(element);
10504 #if USE_STOP_CHANGED_ELEMENTS
10505 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10507 int old_element = Feld[x][y];
10509 /* prevent changed element from moving in same engine frame
10510 unless both old and new element can either fall or move */
10511 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10512 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10517 CreateFieldExt(x, y, element, TRUE);
10520 static boolean ChangeElement(int x, int y, int element, int page)
10522 struct ElementInfo *ei = &element_info[element];
10523 struct ElementChangeInfo *change = &ei->change_page[page];
10524 int ce_value = CustomValue[x][y];
10525 int ce_score = ei->collect_score;
10526 int target_element;
10527 int old_element = Feld[x][y];
10529 /* always use default change event to prevent running into a loop */
10530 if (ChangeEvent[x][y] == -1)
10531 ChangeEvent[x][y] = CE_DELAY;
10533 if (ChangeEvent[x][y] == CE_DELAY)
10535 /* reset actual trigger element, trigger player and action element */
10536 change->actual_trigger_element = EL_EMPTY;
10537 change->actual_trigger_player = EL_PLAYER_1;
10538 change->actual_trigger_player_bits = CH_PLAYER_1;
10539 change->actual_trigger_side = CH_SIDE_NONE;
10540 change->actual_trigger_ce_value = 0;
10541 change->actual_trigger_ce_score = 0;
10544 /* do not change elements more than a specified maximum number of changes */
10545 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10548 ChangeCount[x][y]++; /* count number of changes in the same frame */
10550 if (change->explode)
10557 if (change->use_target_content)
10559 boolean complete_replace = TRUE;
10560 boolean can_replace[3][3];
10563 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10566 boolean is_walkable;
10567 boolean is_diggable;
10568 boolean is_collectible;
10569 boolean is_removable;
10570 boolean is_destructible;
10571 int ex = x + xx - 1;
10572 int ey = y + yy - 1;
10573 int content_element = change->target_content.e[xx][yy];
10576 can_replace[xx][yy] = TRUE;
10578 if (ex == x && ey == y) /* do not check changing element itself */
10581 if (content_element == EL_EMPTY_SPACE)
10583 can_replace[xx][yy] = FALSE; /* do not replace border with space */
10588 if (!IN_LEV_FIELD(ex, ey))
10590 can_replace[xx][yy] = FALSE;
10591 complete_replace = FALSE;
10598 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10599 e = MovingOrBlocked2Element(ex, ey);
10601 is_empty = (IS_FREE(ex, ey) ||
10602 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10604 is_walkable = (is_empty || IS_WALKABLE(e));
10605 is_diggable = (is_empty || IS_DIGGABLE(e));
10606 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10607 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10608 is_removable = (is_diggable || is_collectible);
10610 can_replace[xx][yy] =
10611 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10612 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10613 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10614 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10615 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10616 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10617 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10619 if (!can_replace[xx][yy])
10620 complete_replace = FALSE;
10623 if (!change->only_if_complete || complete_replace)
10625 boolean something_has_changed = FALSE;
10627 if (change->only_if_complete && change->use_random_replace &&
10628 RND(100) < change->random_percentage)
10631 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10633 int ex = x + xx - 1;
10634 int ey = y + yy - 1;
10635 int content_element;
10637 if (can_replace[xx][yy] && (!change->use_random_replace ||
10638 RND(100) < change->random_percentage))
10640 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10641 RemoveMovingField(ex, ey);
10643 ChangeEvent[ex][ey] = ChangeEvent[x][y];
10645 content_element = change->target_content.e[xx][yy];
10646 target_element = GET_TARGET_ELEMENT(element, content_element, change,
10647 ce_value, ce_score);
10649 CreateElementFromChange(ex, ey, target_element);
10651 something_has_changed = TRUE;
10653 /* for symmetry reasons, freeze newly created border elements */
10654 if (ex != x || ey != y)
10655 Stop[ex][ey] = TRUE; /* no more moving in this frame */
10659 if (something_has_changed)
10661 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10662 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10668 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10669 ce_value, ce_score);
10671 if (element == EL_DIAGONAL_GROWING ||
10672 element == EL_DIAGONAL_SHRINKING)
10674 target_element = Store[x][y];
10676 Store[x][y] = EL_EMPTY;
10679 CreateElementFromChange(x, y, target_element);
10681 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10682 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10685 /* this uses direct change before indirect change */
10686 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10691 #if USE_NEW_DELAYED_ACTION
10693 static void HandleElementChange(int x, int y, int page)
10695 int element = MovingOrBlocked2Element(x, y);
10696 struct ElementInfo *ei = &element_info[element];
10697 struct ElementChangeInfo *change = &ei->change_page[page];
10700 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10701 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10704 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10705 x, y, element, element_info[element].token_name);
10706 printf("HandleElementChange(): This should never happen!\n");
10711 /* this can happen with classic bombs on walkable, changing elements */
10712 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10715 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
10716 ChangeDelay[x][y] = 0;
10722 if (ChangeDelay[x][y] == 0) /* initialize element change */
10724 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10726 if (change->can_change)
10729 /* !!! not clear why graphic animation should be reset at all here !!! */
10730 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10731 #if USE_GFX_RESET_WHEN_NOT_MOVING
10732 /* when a custom element is about to change (for example by change delay),
10733 do not reset graphic animation when the custom element is moving */
10734 if (!IS_MOVING(x, y))
10737 ResetGfxAnimation(x, y);
10738 ResetRandomAnimationValue(x, y);
10742 if (change->pre_change_function)
10743 change->pre_change_function(x, y);
10747 ChangeDelay[x][y]--;
10749 if (ChangeDelay[x][y] != 0) /* continue element change */
10751 if (change->can_change)
10753 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10755 if (IS_ANIMATED(graphic))
10756 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10758 if (change->change_function)
10759 change->change_function(x, y);
10762 else /* finish element change */
10764 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10766 page = ChangePage[x][y];
10767 ChangePage[x][y] = -1;
10769 change = &ei->change_page[page];
10772 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10774 ChangeDelay[x][y] = 1; /* try change after next move step */
10775 ChangePage[x][y] = page; /* remember page to use for change */
10780 if (change->can_change)
10782 if (ChangeElement(x, y, element, page))
10784 if (change->post_change_function)
10785 change->post_change_function(x, y);
10789 if (change->has_action)
10790 ExecuteCustomElementAction(x, y, element, page);
10796 static void HandleElementChange(int x, int y, int page)
10798 int element = MovingOrBlocked2Element(x, y);
10799 struct ElementInfo *ei = &element_info[element];
10800 struct ElementChangeInfo *change = &ei->change_page[page];
10803 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
10806 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10807 x, y, element, element_info[element].token_name);
10808 printf("HandleElementChange(): This should never happen!\n");
10813 /* this can happen with classic bombs on walkable, changing elements */
10814 if (!CAN_CHANGE(element))
10817 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
10818 ChangeDelay[x][y] = 0;
10824 if (ChangeDelay[x][y] == 0) /* initialize element change */
10826 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10828 ResetGfxAnimation(x, y);
10829 ResetRandomAnimationValue(x, y);
10831 if (change->pre_change_function)
10832 change->pre_change_function(x, y);
10835 ChangeDelay[x][y]--;
10837 if (ChangeDelay[x][y] != 0) /* continue element change */
10839 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10841 if (IS_ANIMATED(graphic))
10842 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10844 if (change->change_function)
10845 change->change_function(x, y);
10847 else /* finish element change */
10849 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10851 page = ChangePage[x][y];
10852 ChangePage[x][y] = -1;
10854 change = &ei->change_page[page];
10857 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10859 ChangeDelay[x][y] = 1; /* try change after next move step */
10860 ChangePage[x][y] = page; /* remember page to use for change */
10865 if (ChangeElement(x, y, element, page))
10867 if (change->post_change_function)
10868 change->post_change_function(x, y);
10875 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10876 int trigger_element,
10878 int trigger_player,
10882 boolean change_done_any = FALSE;
10883 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10886 if (!(trigger_events[trigger_element][trigger_event]))
10890 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10891 trigger_event, recursion_loop_depth, recursion_loop_detected,
10892 recursion_loop_element, EL_NAME(recursion_loop_element));
10895 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10897 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10899 int element = EL_CUSTOM_START + i;
10900 boolean change_done = FALSE;
10903 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10904 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10907 for (p = 0; p < element_info[element].num_change_pages; p++)
10909 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10911 if (change->can_change_or_has_action &&
10912 change->has_event[trigger_event] &&
10913 change->trigger_side & trigger_side &&
10914 change->trigger_player & trigger_player &&
10915 change->trigger_page & trigger_page_bits &&
10916 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10918 change->actual_trigger_element = trigger_element;
10919 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10920 change->actual_trigger_player_bits = trigger_player;
10921 change->actual_trigger_side = trigger_side;
10922 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10923 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10925 if ((change->can_change && !change_done) || change->has_action)
10929 SCAN_PLAYFIELD(x, y)
10931 if (Feld[x][y] == element)
10933 if (change->can_change && !change_done)
10935 ChangeDelay[x][y] = 1;
10936 ChangeEvent[x][y] = trigger_event;
10938 HandleElementChange(x, y, p);
10940 #if USE_NEW_DELAYED_ACTION
10941 else if (change->has_action)
10943 ExecuteCustomElementAction(x, y, element, p);
10944 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10947 if (change->has_action)
10949 ExecuteCustomElementAction(x, y, element, p);
10950 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10956 if (change->can_change)
10958 change_done = TRUE;
10959 change_done_any = TRUE;
10966 RECURSION_LOOP_DETECTION_END();
10968 return change_done_any;
10971 static boolean CheckElementChangeExt(int x, int y,
10973 int trigger_element,
10975 int trigger_player,
10978 boolean change_done = FALSE;
10981 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10982 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10985 if (Feld[x][y] == EL_BLOCKED)
10987 Blocked2Moving(x, y, &x, &y);
10988 element = Feld[x][y];
10992 /* check if element has already changed */
10993 if (Feld[x][y] != element)
10996 /* check if element has already changed or is about to change after moving */
10997 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10998 Feld[x][y] != element) ||
11000 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11001 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11002 ChangePage[x][y] != -1)))
11007 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11008 trigger_event, recursion_loop_depth, recursion_loop_detected,
11009 recursion_loop_element, EL_NAME(recursion_loop_element));
11012 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11014 for (p = 0; p < element_info[element].num_change_pages; p++)
11016 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11018 /* check trigger element for all events where the element that is checked
11019 for changing interacts with a directly adjacent element -- this is
11020 different to element changes that affect other elements to change on the
11021 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11022 boolean check_trigger_element =
11023 (trigger_event == CE_TOUCHING_X ||
11024 trigger_event == CE_HITTING_X ||
11025 trigger_event == CE_HIT_BY_X ||
11027 /* this one was forgotten until 3.2.3 */
11028 trigger_event == CE_DIGGING_X);
11031 if (change->can_change_or_has_action &&
11032 change->has_event[trigger_event] &&
11033 change->trigger_side & trigger_side &&
11034 change->trigger_player & trigger_player &&
11035 (!check_trigger_element ||
11036 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11038 change->actual_trigger_element = trigger_element;
11039 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11040 change->actual_trigger_player_bits = trigger_player;
11041 change->actual_trigger_side = trigger_side;
11042 change->actual_trigger_ce_value = CustomValue[x][y];
11043 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11045 /* special case: trigger element not at (x,y) position for some events */
11046 if (check_trigger_element)
11058 { 0, 0 }, { 0, 0 }, { 0, 0 },
11062 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11063 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11065 change->actual_trigger_ce_value = CustomValue[xx][yy];
11066 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11069 if (change->can_change && !change_done)
11071 ChangeDelay[x][y] = 1;
11072 ChangeEvent[x][y] = trigger_event;
11074 HandleElementChange(x, y, p);
11076 change_done = TRUE;
11078 #if USE_NEW_DELAYED_ACTION
11079 else if (change->has_action)
11081 ExecuteCustomElementAction(x, y, element, p);
11082 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11085 if (change->has_action)
11087 ExecuteCustomElementAction(x, y, element, p);
11088 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11094 RECURSION_LOOP_DETECTION_END();
11096 return change_done;
11099 static void PlayPlayerSound(struct PlayerInfo *player)
11101 int jx = player->jx, jy = player->jy;
11102 int sound_element = player->artwork_element;
11103 int last_action = player->last_action_waiting;
11104 int action = player->action_waiting;
11106 if (player->is_waiting)
11108 if (action != last_action)
11109 PlayLevelSoundElementAction(jx, jy, sound_element, action);
11111 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11115 if (action != last_action)
11116 StopSound(element_info[sound_element].sound[last_action]);
11118 if (last_action == ACTION_SLEEPING)
11119 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11123 static void PlayAllPlayersSound()
11127 for (i = 0; i < MAX_PLAYERS; i++)
11128 if (stored_player[i].active)
11129 PlayPlayerSound(&stored_player[i]);
11132 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11134 boolean last_waiting = player->is_waiting;
11135 int move_dir = player->MovDir;
11137 player->dir_waiting = move_dir;
11138 player->last_action_waiting = player->action_waiting;
11142 if (!last_waiting) /* not waiting -> waiting */
11144 player->is_waiting = TRUE;
11146 player->frame_counter_bored =
11148 game.player_boring_delay_fixed +
11149 GetSimpleRandom(game.player_boring_delay_random);
11150 player->frame_counter_sleeping =
11152 game.player_sleeping_delay_fixed +
11153 GetSimpleRandom(game.player_sleeping_delay_random);
11155 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11158 if (game.player_sleeping_delay_fixed +
11159 game.player_sleeping_delay_random > 0 &&
11160 player->anim_delay_counter == 0 &&
11161 player->post_delay_counter == 0 &&
11162 FrameCounter >= player->frame_counter_sleeping)
11163 player->is_sleeping = TRUE;
11164 else if (game.player_boring_delay_fixed +
11165 game.player_boring_delay_random > 0 &&
11166 FrameCounter >= player->frame_counter_bored)
11167 player->is_bored = TRUE;
11169 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11170 player->is_bored ? ACTION_BORING :
11173 if (player->is_sleeping && player->use_murphy)
11175 /* special case for sleeping Murphy when leaning against non-free tile */
11177 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11178 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11179 !IS_MOVING(player->jx - 1, player->jy)))
11180 move_dir = MV_LEFT;
11181 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11182 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11183 !IS_MOVING(player->jx + 1, player->jy)))
11184 move_dir = MV_RIGHT;
11186 player->is_sleeping = FALSE;
11188 player->dir_waiting = move_dir;
11191 if (player->is_sleeping)
11193 if (player->num_special_action_sleeping > 0)
11195 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11197 int last_special_action = player->special_action_sleeping;
11198 int num_special_action = player->num_special_action_sleeping;
11199 int special_action =
11200 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11201 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11202 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11203 last_special_action + 1 : ACTION_SLEEPING);
11204 int special_graphic =
11205 el_act_dir2img(player->artwork_element, special_action, move_dir);
11207 player->anim_delay_counter =
11208 graphic_info[special_graphic].anim_delay_fixed +
11209 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11210 player->post_delay_counter =
11211 graphic_info[special_graphic].post_delay_fixed +
11212 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11214 player->special_action_sleeping = special_action;
11217 if (player->anim_delay_counter > 0)
11219 player->action_waiting = player->special_action_sleeping;
11220 player->anim_delay_counter--;
11222 else if (player->post_delay_counter > 0)
11224 player->post_delay_counter--;
11228 else if (player->is_bored)
11230 if (player->num_special_action_bored > 0)
11232 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11234 int special_action =
11235 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11236 int special_graphic =
11237 el_act_dir2img(player->artwork_element, special_action, move_dir);
11239 player->anim_delay_counter =
11240 graphic_info[special_graphic].anim_delay_fixed +
11241 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11242 player->post_delay_counter =
11243 graphic_info[special_graphic].post_delay_fixed +
11244 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11246 player->special_action_bored = special_action;
11249 if (player->anim_delay_counter > 0)
11251 player->action_waiting = player->special_action_bored;
11252 player->anim_delay_counter--;
11254 else if (player->post_delay_counter > 0)
11256 player->post_delay_counter--;
11261 else if (last_waiting) /* waiting -> not waiting */
11263 player->is_waiting = FALSE;
11264 player->is_bored = FALSE;
11265 player->is_sleeping = FALSE;
11267 player->frame_counter_bored = -1;
11268 player->frame_counter_sleeping = -1;
11270 player->anim_delay_counter = 0;
11271 player->post_delay_counter = 0;
11273 player->dir_waiting = player->MovDir;
11274 player->action_waiting = ACTION_DEFAULT;
11276 player->special_action_bored = ACTION_DEFAULT;
11277 player->special_action_sleeping = ACTION_DEFAULT;
11281 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11283 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11284 int left = player_action & JOY_LEFT;
11285 int right = player_action & JOY_RIGHT;
11286 int up = player_action & JOY_UP;
11287 int down = player_action & JOY_DOWN;
11288 int button1 = player_action & JOY_BUTTON_1;
11289 int button2 = player_action & JOY_BUTTON_2;
11290 int dx = (left ? -1 : right ? 1 : 0);
11291 int dy = (up ? -1 : down ? 1 : 0);
11293 if (!player->active || tape.pausing)
11299 snapped = SnapField(player, dx, dy);
11303 dropped = DropElement(player);
11305 moved = MovePlayer(player, dx, dy);
11308 if (tape.single_step && tape.recording && !tape.pausing)
11310 if (button1 || (dropped && !moved))
11312 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11313 SnapField(player, 0, 0); /* stop snapping */
11317 SetPlayerWaiting(player, FALSE);
11319 return player_action;
11323 /* no actions for this player (no input at player's configured device) */
11325 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11326 SnapField(player, 0, 0);
11327 CheckGravityMovementWhenNotMoving(player);
11329 if (player->MovPos == 0)
11330 SetPlayerWaiting(player, TRUE);
11332 if (player->MovPos == 0) /* needed for tape.playing */
11333 player->is_moving = FALSE;
11335 player->is_dropping = FALSE;
11336 player->is_dropping_pressed = FALSE;
11337 player->drop_pressed_delay = 0;
11343 static void CheckLevelTime()
11347 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11349 if (level.native_em_level->lev->home == 0) /* all players at home */
11351 PlayerWins(local_player);
11353 AllPlayersGone = TRUE;
11355 level.native_em_level->lev->home = -1;
11358 if (level.native_em_level->ply[0]->alive == 0 &&
11359 level.native_em_level->ply[1]->alive == 0 &&
11360 level.native_em_level->ply[2]->alive == 0 &&
11361 level.native_em_level->ply[3]->alive == 0) /* all dead */
11362 AllPlayersGone = TRUE;
11365 if (TimeFrames >= FRAMES_PER_SECOND)
11370 for (i = 0; i < MAX_PLAYERS; i++)
11372 struct PlayerInfo *player = &stored_player[i];
11374 if (SHIELD_ON(player))
11376 player->shield_normal_time_left--;
11378 if (player->shield_deadly_time_left > 0)
11379 player->shield_deadly_time_left--;
11383 if (!local_player->LevelSolved && !level.use_step_counter)
11391 if (TimeLeft <= 10 && setup.time_limit)
11392 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11395 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11397 DisplayGameControlValues();
11399 DrawGameValue_Time(TimeLeft);
11402 if (!TimeLeft && setup.time_limit)
11404 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11405 level.native_em_level->lev->killed_out_of_time = TRUE;
11407 for (i = 0; i < MAX_PLAYERS; i++)
11408 KillPlayer(&stored_player[i]);
11412 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11414 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11416 DisplayGameControlValues();
11419 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11420 DrawGameValue_Time(TimePlayed);
11423 level.native_em_level->lev->time =
11424 (level.time == 0 ? TimePlayed : TimeLeft);
11427 if (tape.recording || tape.playing)
11428 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11431 UpdateGameDoorValues();
11432 DrawGameDoorValues();
11435 void AdvanceFrameAndPlayerCounters(int player_nr)
11439 /* advance frame counters (global frame counter and time frame counter) */
11443 /* advance player counters (counters for move delay, move animation etc.) */
11444 for (i = 0; i < MAX_PLAYERS; i++)
11446 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11447 int move_delay_value = stored_player[i].move_delay_value;
11448 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11450 if (!advance_player_counters) /* not all players may be affected */
11453 #if USE_NEW_PLAYER_ANIM
11454 if (move_frames == 0) /* less than one move per game frame */
11456 int stepsize = TILEX / move_delay_value;
11457 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11458 int count = (stored_player[i].is_moving ?
11459 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11461 if (count % delay == 0)
11466 stored_player[i].Frame += move_frames;
11468 if (stored_player[i].MovPos != 0)
11469 stored_player[i].StepFrame += move_frames;
11471 if (stored_player[i].move_delay > 0)
11472 stored_player[i].move_delay--;
11474 /* due to bugs in previous versions, counter must count up, not down */
11475 if (stored_player[i].push_delay != -1)
11476 stored_player[i].push_delay++;
11478 if (stored_player[i].drop_delay > 0)
11479 stored_player[i].drop_delay--;
11481 if (stored_player[i].is_dropping_pressed)
11482 stored_player[i].drop_pressed_delay++;
11486 void StartGameActions(boolean init_network_game, boolean record_tape,
11489 unsigned long new_random_seed = InitRND(random_seed);
11492 TapeStartRecording(new_random_seed);
11494 #if defined(NETWORK_AVALIABLE)
11495 if (init_network_game)
11497 SendToServer_StartPlaying();
11508 static unsigned long game_frame_delay = 0;
11509 unsigned long game_frame_delay_value;
11510 byte *recorded_player_action;
11511 byte summarized_player_action = 0;
11512 byte tape_action[MAX_PLAYERS];
11515 /* detect endless loops, caused by custom element programming */
11516 if (recursion_loop_detected && recursion_loop_depth == 0)
11518 char *message = getStringCat3("Internal Error ! Element ",
11519 EL_NAME(recursion_loop_element),
11520 " caused endless loop ! Quit the game ?");
11522 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11523 EL_NAME(recursion_loop_element));
11525 RequestQuitGameExt(FALSE, level_editor_test_game, message);
11527 recursion_loop_detected = FALSE; /* if game should be continued */
11534 if (game.restart_level)
11535 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
11537 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11539 if (level.native_em_level->lev->home == 0) /* all players at home */
11541 PlayerWins(local_player);
11543 AllPlayersGone = TRUE;
11545 level.native_em_level->lev->home = -1;
11548 if (level.native_em_level->ply[0]->alive == 0 &&
11549 level.native_em_level->ply[1]->alive == 0 &&
11550 level.native_em_level->ply[2]->alive == 0 &&
11551 level.native_em_level->ply[3]->alive == 0) /* all dead */
11552 AllPlayersGone = TRUE;
11555 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11558 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11561 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
11564 game_frame_delay_value =
11565 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11567 if (tape.playing && tape.warp_forward && !tape.pausing)
11568 game_frame_delay_value = 0;
11570 /* ---------- main game synchronization point ---------- */
11572 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11574 if (network_playing && !network_player_action_received)
11576 /* try to get network player actions in time */
11578 #if defined(NETWORK_AVALIABLE)
11579 /* last chance to get network player actions without main loop delay */
11580 HandleNetworking();
11583 /* game was quit by network peer */
11584 if (game_status != GAME_MODE_PLAYING)
11587 if (!network_player_action_received)
11588 return; /* failed to get network player actions in time */
11590 /* do not yet reset "network_player_action_received" (for tape.pausing) */
11596 /* at this point we know that we really continue executing the game */
11598 network_player_action_received = FALSE;
11600 /* when playing tape, read previously recorded player input from tape data */
11601 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11604 /* TapePlayAction() may return NULL when toggling to "pause before death" */
11609 if (tape.set_centered_player)
11611 game.centered_player_nr_next = tape.centered_player_nr_next;
11612 game.set_centered_player = TRUE;
11615 for (i = 0; i < MAX_PLAYERS; i++)
11617 summarized_player_action |= stored_player[i].action;
11619 if (!network_playing)
11620 stored_player[i].effective_action = stored_player[i].action;
11623 #if defined(NETWORK_AVALIABLE)
11624 if (network_playing)
11625 SendToServer_MovePlayer(summarized_player_action);
11628 if (!options.network && !setup.team_mode)
11629 local_player->effective_action = summarized_player_action;
11631 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
11633 for (i = 0; i < MAX_PLAYERS; i++)
11634 stored_player[i].effective_action =
11635 (i == game.centered_player_nr ? summarized_player_action : 0);
11638 if (recorded_player_action != NULL)
11639 for (i = 0; i < MAX_PLAYERS; i++)
11640 stored_player[i].effective_action = recorded_player_action[i];
11642 for (i = 0; i < MAX_PLAYERS; i++)
11644 tape_action[i] = stored_player[i].effective_action;
11646 /* (this can only happen in the R'n'D game engine) */
11647 if (tape.recording && tape_action[i] && !tape.player_participates[i])
11648 tape.player_participates[i] = TRUE; /* player just appeared from CE */
11651 /* only record actions from input devices, but not programmed actions */
11652 if (tape.recording)
11653 TapeRecordAction(tape_action);
11655 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11657 GameActions_EM_Main();
11665 void GameActions_EM_Main()
11667 byte effective_action[MAX_PLAYERS];
11668 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11671 for (i = 0; i < MAX_PLAYERS; i++)
11672 effective_action[i] = stored_player[i].effective_action;
11674 GameActions_EM(effective_action, warp_mode);
11678 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11681 void GameActions_RND()
11683 int magic_wall_x = 0, magic_wall_y = 0;
11684 int i, x, y, element, graphic;
11686 InitPlayfieldScanModeVars();
11688 #if USE_ONE_MORE_CHANGE_PER_FRAME
11689 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11691 SCAN_PLAYFIELD(x, y)
11693 ChangeCount[x][y] = 0;
11694 ChangeEvent[x][y] = -1;
11699 if (game.set_centered_player)
11701 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11703 /* switching to "all players" only possible if all players fit to screen */
11704 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11706 game.centered_player_nr_next = game.centered_player_nr;
11707 game.set_centered_player = FALSE;
11710 /* do not switch focus to non-existing (or non-active) player */
11711 if (game.centered_player_nr_next >= 0 &&
11712 !stored_player[game.centered_player_nr_next].active)
11714 game.centered_player_nr_next = game.centered_player_nr;
11715 game.set_centered_player = FALSE;
11719 if (game.set_centered_player &&
11720 ScreenMovPos == 0) /* screen currently aligned at tile position */
11724 if (game.centered_player_nr_next == -1)
11726 setScreenCenteredToAllPlayers(&sx, &sy);
11730 sx = stored_player[game.centered_player_nr_next].jx;
11731 sy = stored_player[game.centered_player_nr_next].jy;
11734 game.centered_player_nr = game.centered_player_nr_next;
11735 game.set_centered_player = FALSE;
11737 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11738 DrawGameDoorValues();
11741 for (i = 0; i < MAX_PLAYERS; i++)
11743 int actual_player_action = stored_player[i].effective_action;
11746 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11747 - rnd_equinox_tetrachloride 048
11748 - rnd_equinox_tetrachloride_ii 096
11749 - rnd_emanuel_schmieg 002
11750 - doctor_sloan_ww 001, 020
11752 if (stored_player[i].MovPos == 0)
11753 CheckGravityMovement(&stored_player[i]);
11756 /* overwrite programmed action with tape action */
11757 if (stored_player[i].programmed_action)
11758 actual_player_action = stored_player[i].programmed_action;
11760 PlayerActions(&stored_player[i], actual_player_action);
11762 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11765 ScrollScreen(NULL, SCROLL_GO_ON);
11767 /* for backwards compatibility, the following code emulates a fixed bug that
11768 occured when pushing elements (causing elements that just made their last
11769 pushing step to already (if possible) make their first falling step in the
11770 same game frame, which is bad); this code is also needed to use the famous
11771 "spring push bug" which is used in older levels and might be wanted to be
11772 used also in newer levels, but in this case the buggy pushing code is only
11773 affecting the "spring" element and no other elements */
11775 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11777 for (i = 0; i < MAX_PLAYERS; i++)
11779 struct PlayerInfo *player = &stored_player[i];
11780 int x = player->jx;
11781 int y = player->jy;
11783 if (player->active && player->is_pushing && player->is_moving &&
11785 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11786 Feld[x][y] == EL_SPRING))
11788 ContinueMoving(x, y);
11790 /* continue moving after pushing (this is actually a bug) */
11791 if (!IS_MOVING(x, y))
11792 Stop[x][y] = FALSE;
11798 debug_print_timestamp(0, "start main loop profiling");
11801 SCAN_PLAYFIELD(x, y)
11803 ChangeCount[x][y] = 0;
11804 ChangeEvent[x][y] = -1;
11806 /* this must be handled before main playfield loop */
11807 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11810 if (MovDelay[x][y] <= 0)
11814 #if USE_NEW_SNAP_DELAY
11815 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11818 if (MovDelay[x][y] <= 0)
11821 DrawLevelField(x, y);
11823 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11829 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11831 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11832 printf("GameActions(): This should never happen!\n");
11834 ChangePage[x][y] = -1;
11838 Stop[x][y] = FALSE;
11839 if (WasJustMoving[x][y] > 0)
11840 WasJustMoving[x][y]--;
11841 if (WasJustFalling[x][y] > 0)
11842 WasJustFalling[x][y]--;
11843 if (CheckCollision[x][y] > 0)
11844 CheckCollision[x][y]--;
11845 if (CheckImpact[x][y] > 0)
11846 CheckImpact[x][y]--;
11850 /* reset finished pushing action (not done in ContinueMoving() to allow
11851 continuous pushing animation for elements with zero push delay) */
11852 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11854 ResetGfxAnimation(x, y);
11855 DrawLevelField(x, y);
11859 if (IS_BLOCKED(x, y))
11863 Blocked2Moving(x, y, &oldx, &oldy);
11864 if (!IS_MOVING(oldx, oldy))
11866 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11867 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11868 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11869 printf("GameActions(): This should never happen!\n");
11876 debug_print_timestamp(0, "- time for pre-main loop:");
11879 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
11880 SCAN_PLAYFIELD(x, y)
11882 element = Feld[x][y];
11883 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11888 int element2 = element;
11889 int graphic2 = graphic;
11891 int element2 = Feld[x][y];
11892 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
11894 int last_gfx_frame = GfxFrame[x][y];
11896 if (graphic_info[graphic2].anim_global_sync)
11897 GfxFrame[x][y] = FrameCounter;
11898 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
11899 GfxFrame[x][y] = CustomValue[x][y];
11900 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
11901 GfxFrame[x][y] = element_info[element2].collect_score;
11902 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
11903 GfxFrame[x][y] = ChangeDelay[x][y];
11905 if (redraw && GfxFrame[x][y] != last_gfx_frame)
11906 DrawLevelGraphicAnimation(x, y, graphic2);
11909 ResetGfxFrame(x, y, TRUE);
11913 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11914 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11915 ResetRandomAnimationValue(x, y);
11919 SetRandomAnimationValue(x, y);
11923 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11926 #endif // -------------------- !!! TEST ONLY !!! --------------------
11929 debug_print_timestamp(0, "- time for TEST loop: -->");
11932 SCAN_PLAYFIELD(x, y)
11934 element = Feld[x][y];
11935 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11937 ResetGfxFrame(x, y, TRUE);
11939 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11940 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11941 ResetRandomAnimationValue(x, y);
11943 SetRandomAnimationValue(x, y);
11945 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11947 if (IS_INACTIVE(element))
11949 if (IS_ANIMATED(graphic))
11950 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11955 /* this may take place after moving, so 'element' may have changed */
11956 if (IS_CHANGING(x, y) &&
11957 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11959 int page = element_info[element].event_page_nr[CE_DELAY];
11962 HandleElementChange(x, y, page);
11964 if (CAN_CHANGE(element))
11965 HandleElementChange(x, y, page);
11967 if (HAS_ACTION(element))
11968 ExecuteCustomElementAction(x, y, element, page);
11971 element = Feld[x][y];
11972 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11975 #if 0 // ---------------------------------------------------------------------
11977 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11981 element = Feld[x][y];
11982 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11984 if (IS_ANIMATED(graphic) &&
11985 !IS_MOVING(x, y) &&
11987 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11989 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11990 DrawTwinkleOnField(x, y);
11992 else if (IS_MOVING(x, y))
11993 ContinueMoving(x, y);
12000 case EL_EM_EXIT_OPEN:
12001 case EL_SP_EXIT_OPEN:
12002 case EL_STEEL_EXIT_OPEN:
12003 case EL_EM_STEEL_EXIT_OPEN:
12004 case EL_SP_TERMINAL:
12005 case EL_SP_TERMINAL_ACTIVE:
12006 case EL_EXTRA_TIME:
12007 case EL_SHIELD_NORMAL:
12008 case EL_SHIELD_DEADLY:
12009 if (IS_ANIMATED(graphic))
12010 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12013 case EL_DYNAMITE_ACTIVE:
12014 case EL_EM_DYNAMITE_ACTIVE:
12015 case EL_DYNABOMB_PLAYER_1_ACTIVE:
12016 case EL_DYNABOMB_PLAYER_2_ACTIVE:
12017 case EL_DYNABOMB_PLAYER_3_ACTIVE:
12018 case EL_DYNABOMB_PLAYER_4_ACTIVE:
12019 case EL_SP_DISK_RED_ACTIVE:
12020 CheckDynamite(x, y);
12023 case EL_AMOEBA_GROWING:
12024 AmoebeWaechst(x, y);
12027 case EL_AMOEBA_SHRINKING:
12028 AmoebaDisappearing(x, y);
12031 #if !USE_NEW_AMOEBA_CODE
12032 case EL_AMOEBA_WET:
12033 case EL_AMOEBA_DRY:
12034 case EL_AMOEBA_FULL:
12036 case EL_EMC_DRIPPER:
12037 AmoebeAbleger(x, y);
12041 case EL_GAME_OF_LIFE:
12046 case EL_EXIT_CLOSED:
12050 case EL_EM_EXIT_CLOSED:
12054 case EL_STEEL_EXIT_CLOSED:
12055 CheckExitSteel(x, y);
12058 case EL_EM_STEEL_EXIT_CLOSED:
12059 CheckExitSteelEM(x, y);
12062 case EL_SP_EXIT_CLOSED:
12066 case EL_EXPANDABLE_WALL_GROWING:
12067 case EL_EXPANDABLE_STEELWALL_GROWING:
12068 MauerWaechst(x, y);
12071 case EL_EXPANDABLE_WALL:
12072 case EL_EXPANDABLE_WALL_HORIZONTAL:
12073 case EL_EXPANDABLE_WALL_VERTICAL:
12074 case EL_EXPANDABLE_WALL_ANY:
12075 case EL_BD_EXPANDABLE_WALL:
12076 MauerAbleger(x, y);
12079 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12080 case EL_EXPANDABLE_STEELWALL_VERTICAL:
12081 case EL_EXPANDABLE_STEELWALL_ANY:
12082 MauerAblegerStahl(x, y);
12086 CheckForDragon(x, y);
12092 case EL_ELEMENT_SNAPPING:
12093 case EL_DIAGONAL_SHRINKING:
12094 case EL_DIAGONAL_GROWING:
12097 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12099 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12104 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12105 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12110 #else // ---------------------------------------------------------------------
12112 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12116 element = Feld[x][y];
12117 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12119 if (IS_ANIMATED(graphic) &&
12120 !IS_MOVING(x, y) &&
12122 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12124 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12125 DrawTwinkleOnField(x, y);
12127 else if ((element == EL_ACID ||
12128 element == EL_EXIT_OPEN ||
12129 element == EL_EM_EXIT_OPEN ||
12130 element == EL_SP_EXIT_OPEN ||
12131 element == EL_STEEL_EXIT_OPEN ||
12132 element == EL_EM_STEEL_EXIT_OPEN ||
12133 element == EL_SP_TERMINAL ||
12134 element == EL_SP_TERMINAL_ACTIVE ||
12135 element == EL_EXTRA_TIME ||
12136 element == EL_SHIELD_NORMAL ||
12137 element == EL_SHIELD_DEADLY) &&
12138 IS_ANIMATED(graphic))
12139 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12140 else if (IS_MOVING(x, y))
12141 ContinueMoving(x, y);
12142 else if (IS_ACTIVE_BOMB(element))
12143 CheckDynamite(x, y);
12144 else if (element == EL_AMOEBA_GROWING)
12145 AmoebeWaechst(x, y);
12146 else if (element == EL_AMOEBA_SHRINKING)
12147 AmoebaDisappearing(x, y);
12149 #if !USE_NEW_AMOEBA_CODE
12150 else if (IS_AMOEBALIVE(element))
12151 AmoebeAbleger(x, y);
12154 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12156 else if (element == EL_EXIT_CLOSED)
12158 else if (element == EL_EM_EXIT_CLOSED)
12160 else if (element == EL_STEEL_EXIT_CLOSED)
12161 CheckExitSteel(x, y);
12162 else if (element == EL_EM_STEEL_EXIT_CLOSED)
12163 CheckExitSteelEM(x, y);
12164 else if (element == EL_SP_EXIT_CLOSED)
12166 else if (element == EL_EXPANDABLE_WALL_GROWING ||
12167 element == EL_EXPANDABLE_STEELWALL_GROWING)
12168 MauerWaechst(x, y);
12169 else if (element == EL_EXPANDABLE_WALL ||
12170 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12171 element == EL_EXPANDABLE_WALL_VERTICAL ||
12172 element == EL_EXPANDABLE_WALL_ANY ||
12173 element == EL_BD_EXPANDABLE_WALL)
12174 MauerAbleger(x, y);
12175 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12176 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12177 element == EL_EXPANDABLE_STEELWALL_ANY)
12178 MauerAblegerStahl(x, y);
12179 else if (element == EL_FLAMES)
12180 CheckForDragon(x, y);
12181 else if (element == EL_EXPLOSION)
12182 ; /* drawing of correct explosion animation is handled separately */
12183 else if (element == EL_ELEMENT_SNAPPING ||
12184 element == EL_DIAGONAL_SHRINKING ||
12185 element == EL_DIAGONAL_GROWING)
12187 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12189 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12191 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12192 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12194 #endif // ---------------------------------------------------------------------
12196 if (IS_BELT_ACTIVE(element))
12197 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12199 if (game.magic_wall_active)
12201 int jx = local_player->jx, jy = local_player->jy;
12203 /* play the element sound at the position nearest to the player */
12204 if ((element == EL_MAGIC_WALL_FULL ||
12205 element == EL_MAGIC_WALL_ACTIVE ||
12206 element == EL_MAGIC_WALL_EMPTYING ||
12207 element == EL_BD_MAGIC_WALL_FULL ||
12208 element == EL_BD_MAGIC_WALL_ACTIVE ||
12209 element == EL_BD_MAGIC_WALL_EMPTYING ||
12210 element == EL_DC_MAGIC_WALL_FULL ||
12211 element == EL_DC_MAGIC_WALL_ACTIVE ||
12212 element == EL_DC_MAGIC_WALL_EMPTYING) &&
12213 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12222 debug_print_timestamp(0, "- time for MAIN loop: -->");
12225 #if USE_NEW_AMOEBA_CODE
12226 /* new experimental amoeba growth stuff */
12227 if (!(FrameCounter % 8))
12229 static unsigned long random = 1684108901;
12231 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12233 x = RND(lev_fieldx);
12234 y = RND(lev_fieldy);
12235 element = Feld[x][y];
12237 if (!IS_PLAYER(x,y) &&
12238 (element == EL_EMPTY ||
12239 CAN_GROW_INTO(element) ||
12240 element == EL_QUICKSAND_EMPTY ||
12241 element == EL_QUICKSAND_FAST_EMPTY ||
12242 element == EL_ACID_SPLASH_LEFT ||
12243 element == EL_ACID_SPLASH_RIGHT))
12245 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12246 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12247 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12248 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12249 Feld[x][y] = EL_AMOEBA_DROP;
12252 random = random * 129 + 1;
12258 if (game.explosions_delayed)
12261 game.explosions_delayed = FALSE;
12263 SCAN_PLAYFIELD(x, y)
12265 element = Feld[x][y];
12267 if (ExplodeField[x][y])
12268 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12269 else if (element == EL_EXPLOSION)
12270 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12272 ExplodeField[x][y] = EX_TYPE_NONE;
12275 game.explosions_delayed = TRUE;
12278 if (game.magic_wall_active)
12280 if (!(game.magic_wall_time_left % 4))
12282 int element = Feld[magic_wall_x][magic_wall_y];
12284 if (element == EL_BD_MAGIC_WALL_FULL ||
12285 element == EL_BD_MAGIC_WALL_ACTIVE ||
12286 element == EL_BD_MAGIC_WALL_EMPTYING)
12287 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12288 else if (element == EL_DC_MAGIC_WALL_FULL ||
12289 element == EL_DC_MAGIC_WALL_ACTIVE ||
12290 element == EL_DC_MAGIC_WALL_EMPTYING)
12291 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12293 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12296 if (game.magic_wall_time_left > 0)
12298 game.magic_wall_time_left--;
12300 if (!game.magic_wall_time_left)
12302 SCAN_PLAYFIELD(x, y)
12304 element = Feld[x][y];
12306 if (element == EL_MAGIC_WALL_ACTIVE ||
12307 element == EL_MAGIC_WALL_FULL)
12309 Feld[x][y] = EL_MAGIC_WALL_DEAD;
12310 DrawLevelField(x, y);
12312 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12313 element == EL_BD_MAGIC_WALL_FULL)
12315 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12316 DrawLevelField(x, y);
12318 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12319 element == EL_DC_MAGIC_WALL_FULL)
12321 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12322 DrawLevelField(x, y);
12326 game.magic_wall_active = FALSE;
12331 if (game.light_time_left > 0)
12333 game.light_time_left--;
12335 if (game.light_time_left == 0)
12336 RedrawAllLightSwitchesAndInvisibleElements();
12339 if (game.timegate_time_left > 0)
12341 game.timegate_time_left--;
12343 if (game.timegate_time_left == 0)
12344 CloseAllOpenTimegates();
12347 if (game.lenses_time_left > 0)
12349 game.lenses_time_left--;
12351 if (game.lenses_time_left == 0)
12352 RedrawAllInvisibleElementsForLenses();
12355 if (game.magnify_time_left > 0)
12357 game.magnify_time_left--;
12359 if (game.magnify_time_left == 0)
12360 RedrawAllInvisibleElementsForMagnifier();
12363 for (i = 0; i < MAX_PLAYERS; i++)
12365 struct PlayerInfo *player = &stored_player[i];
12367 if (SHIELD_ON(player))
12369 if (player->shield_deadly_time_left)
12370 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12371 else if (player->shield_normal_time_left)
12372 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12379 PlayAllPlayersSound();
12381 if (options.debug) /* calculate frames per second */
12383 static unsigned long fps_counter = 0;
12384 static int fps_frames = 0;
12385 unsigned long fps_delay_ms = Counter() - fps_counter;
12389 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
12391 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12394 fps_counter = Counter();
12397 redraw_mask |= REDRAW_FPS;
12400 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12402 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12404 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12406 local_player->show_envelope = 0;
12410 debug_print_timestamp(0, "stop main loop profiling ");
12411 printf("----------------------------------------------------------\n");
12414 /* use random number generator in every frame to make it less predictable */
12415 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12419 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12421 int min_x = x, min_y = y, max_x = x, max_y = y;
12424 for (i = 0; i < MAX_PLAYERS; i++)
12426 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12428 if (!stored_player[i].active || &stored_player[i] == player)
12431 min_x = MIN(min_x, jx);
12432 min_y = MIN(min_y, jy);
12433 max_x = MAX(max_x, jx);
12434 max_y = MAX(max_y, jy);
12437 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12440 static boolean AllPlayersInVisibleScreen()
12444 for (i = 0; i < MAX_PLAYERS; i++)
12446 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12448 if (!stored_player[i].active)
12451 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12458 void ScrollLevel(int dx, int dy)
12461 static Bitmap *bitmap_db_field2 = NULL;
12462 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12469 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
12470 /* only horizontal XOR vertical scroll direction allowed */
12471 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
12476 if (bitmap_db_field2 == NULL)
12477 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
12479 /* needed when blitting directly to same bitmap -- should not be needed with
12480 recent SDL libraries, but apparently does not work in 1.2.11 directly */
12481 BlitBitmap(drawto_field, bitmap_db_field2,
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);
12488 BlitBitmap(bitmap_db_field2, drawto_field,
12489 FX + TILEX * (dx == 1) - softscroll_offset,
12490 FY + TILEY * (dy == 1) - softscroll_offset,
12491 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12492 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12493 FX + TILEX * (dx == 1) - softscroll_offset,
12494 FY + TILEY * (dy == 1) - softscroll_offset);
12499 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
12500 int xsize = (BX2 - BX1 + 1);
12501 int ysize = (BY2 - BY1 + 1);
12502 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
12503 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
12504 int step = (start < end ? +1 : -1);
12506 for (i = start; i != end; i += step)
12508 BlitBitmap(drawto_field, drawto_field,
12509 FX + TILEX * (dx != 0 ? i + step : 0),
12510 FY + TILEY * (dy != 0 ? i + step : 0),
12511 TILEX * (dx != 0 ? 1 : xsize),
12512 TILEY * (dy != 0 ? 1 : ysize),
12513 FX + TILEX * (dx != 0 ? i : 0),
12514 FY + TILEY * (dy != 0 ? i : 0));
12519 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12521 BlitBitmap(drawto_field, drawto_field,
12522 FX + TILEX * (dx == -1) - softscroll_offset,
12523 FY + TILEY * (dy == -1) - softscroll_offset,
12524 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12525 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12526 FX + TILEX * (dx == 1) - softscroll_offset,
12527 FY + TILEY * (dy == 1) - softscroll_offset);
12533 x = (dx == 1 ? BX1 : BX2);
12534 for (y = BY1; y <= BY2; y++)
12535 DrawScreenField(x, y);
12540 y = (dy == 1 ? BY1 : BY2);
12541 for (x = BX1; x <= BX2; x++)
12542 DrawScreenField(x, y);
12545 redraw_mask |= REDRAW_FIELD;
12548 static boolean canFallDown(struct PlayerInfo *player)
12550 int jx = player->jx, jy = player->jy;
12552 return (IN_LEV_FIELD(jx, jy + 1) &&
12553 (IS_FREE(jx, jy + 1) ||
12554 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12555 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12556 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12559 static boolean canPassField(int x, int y, int move_dir)
12561 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12562 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12563 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12564 int nextx = x + dx;
12565 int nexty = y + dy;
12566 int element = Feld[x][y];
12568 return (IS_PASSABLE_FROM(element, opposite_dir) &&
12569 !CAN_MOVE(element) &&
12570 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12571 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12572 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12575 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12577 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12578 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12579 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12583 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12584 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12585 (IS_DIGGABLE(Feld[newx][newy]) ||
12586 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12587 canPassField(newx, newy, move_dir)));
12590 static void CheckGravityMovement(struct PlayerInfo *player)
12592 #if USE_PLAYER_GRAVITY
12593 if (player->gravity && !player->programmed_action)
12595 if (game.gravity && !player->programmed_action)
12598 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12599 int move_dir_vertical = player->effective_action & MV_VERTICAL;
12600 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12601 int jx = player->jx, jy = player->jy;
12602 boolean player_is_moving_to_valid_field =
12603 (!player_is_snapping &&
12604 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12605 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12606 boolean player_can_fall_down = canFallDown(player);
12608 if (player_can_fall_down &&
12609 !player_is_moving_to_valid_field)
12610 player->programmed_action = MV_DOWN;
12614 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12616 return CheckGravityMovement(player);
12618 #if USE_PLAYER_GRAVITY
12619 if (player->gravity && !player->programmed_action)
12621 if (game.gravity && !player->programmed_action)
12624 int jx = player->jx, jy = player->jy;
12625 boolean field_under_player_is_free =
12626 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12627 boolean player_is_standing_on_valid_field =
12628 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12629 (IS_WALKABLE(Feld[jx][jy]) &&
12630 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12632 if (field_under_player_is_free && !player_is_standing_on_valid_field)
12633 player->programmed_action = MV_DOWN;
12638 MovePlayerOneStep()
12639 -----------------------------------------------------------------------------
12640 dx, dy: direction (non-diagonal) to try to move the player to
12641 real_dx, real_dy: direction as read from input device (can be diagonal)
12644 boolean MovePlayerOneStep(struct PlayerInfo *player,
12645 int dx, int dy, int real_dx, int real_dy)
12647 int jx = player->jx, jy = player->jy;
12648 int new_jx = jx + dx, new_jy = jy + dy;
12649 #if !USE_FIXED_DONT_RUN_INTO
12653 boolean player_can_move = !player->cannot_move;
12655 if (!player->active || (!dx && !dy))
12656 return MP_NO_ACTION;
12658 player->MovDir = (dx < 0 ? MV_LEFT :
12659 dx > 0 ? MV_RIGHT :
12661 dy > 0 ? MV_DOWN : MV_NONE);
12663 if (!IN_LEV_FIELD(new_jx, new_jy))
12664 return MP_NO_ACTION;
12666 if (!player_can_move)
12668 if (player->MovPos == 0)
12670 player->is_moving = FALSE;
12671 player->is_digging = FALSE;
12672 player->is_collecting = FALSE;
12673 player->is_snapping = FALSE;
12674 player->is_pushing = FALSE;
12679 if (!options.network && game.centered_player_nr == -1 &&
12680 !AllPlayersInSight(player, new_jx, new_jy))
12681 return MP_NO_ACTION;
12683 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
12684 return MP_NO_ACTION;
12687 #if !USE_FIXED_DONT_RUN_INTO
12688 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
12690 /* (moved to DigField()) */
12691 if (player_can_move && DONT_RUN_INTO(element))
12693 if (element == EL_ACID && dx == 0 && dy == 1)
12695 SplashAcid(new_jx, new_jy);
12696 Feld[jx][jy] = EL_PLAYER_1;
12697 InitMovingField(jx, jy, MV_DOWN);
12698 Store[jx][jy] = EL_ACID;
12699 ContinueMoving(jx, jy);
12700 BuryPlayer(player);
12703 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12709 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12710 if (can_move != MP_MOVING)
12713 /* check if DigField() has caused relocation of the player */
12714 if (player->jx != jx || player->jy != jy)
12715 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12717 StorePlayer[jx][jy] = 0;
12718 player->last_jx = jx;
12719 player->last_jy = jy;
12720 player->jx = new_jx;
12721 player->jy = new_jy;
12722 StorePlayer[new_jx][new_jy] = player->element_nr;
12724 if (player->move_delay_value_next != -1)
12726 player->move_delay_value = player->move_delay_value_next;
12727 player->move_delay_value_next = -1;
12731 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12733 player->step_counter++;
12735 PlayerVisit[jx][jy] = FrameCounter;
12737 #if USE_UFAST_PLAYER_EXIT_BUGFIX
12738 player->is_moving = TRUE;
12742 /* should better be called in MovePlayer(), but this breaks some tapes */
12743 ScrollPlayer(player, SCROLL_INIT);
12749 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12751 int jx = player->jx, jy = player->jy;
12752 int old_jx = jx, old_jy = jy;
12753 int moved = MP_NO_ACTION;
12755 if (!player->active)
12760 if (player->MovPos == 0)
12762 player->is_moving = FALSE;
12763 player->is_digging = FALSE;
12764 player->is_collecting = FALSE;
12765 player->is_snapping = FALSE;
12766 player->is_pushing = FALSE;
12772 if (player->move_delay > 0)
12775 player->move_delay = -1; /* set to "uninitialized" value */
12777 /* store if player is automatically moved to next field */
12778 player->is_auto_moving = (player->programmed_action != MV_NONE);
12780 /* remove the last programmed player action */
12781 player->programmed_action = 0;
12783 if (player->MovPos)
12785 /* should only happen if pre-1.2 tape recordings are played */
12786 /* this is only for backward compatibility */
12788 int original_move_delay_value = player->move_delay_value;
12791 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
12795 /* scroll remaining steps with finest movement resolution */
12796 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12798 while (player->MovPos)
12800 ScrollPlayer(player, SCROLL_GO_ON);
12801 ScrollScreen(NULL, SCROLL_GO_ON);
12803 AdvanceFrameAndPlayerCounters(player->index_nr);
12809 player->move_delay_value = original_move_delay_value;
12812 player->is_active = FALSE;
12814 if (player->last_move_dir & MV_HORIZONTAL)
12816 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12817 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12821 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12822 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12825 #if USE_FIXED_BORDER_RUNNING_GFX
12826 if (!moved && !player->is_active)
12828 player->is_moving = FALSE;
12829 player->is_digging = FALSE;
12830 player->is_collecting = FALSE;
12831 player->is_snapping = FALSE;
12832 player->is_pushing = FALSE;
12840 if (moved & MP_MOVING && !ScreenMovPos &&
12841 (player->index_nr == game.centered_player_nr ||
12842 game.centered_player_nr == -1))
12844 if (moved & MP_MOVING && !ScreenMovPos &&
12845 (player == local_player || !options.network))
12848 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12849 int offset = game.scroll_delay_value;
12851 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12853 /* actual player has left the screen -- scroll in that direction */
12854 if (jx != old_jx) /* player has moved horizontally */
12855 scroll_x += (jx - old_jx);
12856 else /* player has moved vertically */
12857 scroll_y += (jy - old_jy);
12861 if (jx != old_jx) /* player has moved horizontally */
12863 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
12864 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12865 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12867 /* don't scroll over playfield boundaries */
12868 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12869 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12871 /* don't scroll more than one field at a time */
12872 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12874 /* don't scroll against the player's moving direction */
12875 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
12876 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12877 scroll_x = old_scroll_x;
12879 else /* player has moved vertically */
12881 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
12882 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12883 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12885 /* don't scroll over playfield boundaries */
12886 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12887 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12889 /* don't scroll more than one field at a time */
12890 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12892 /* don't scroll against the player's moving direction */
12893 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
12894 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12895 scroll_y = old_scroll_y;
12899 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12902 if (!options.network && game.centered_player_nr == -1 &&
12903 !AllPlayersInVisibleScreen())
12905 scroll_x = old_scroll_x;
12906 scroll_y = old_scroll_y;
12910 if (!options.network && !AllPlayersInVisibleScreen())
12912 scroll_x = old_scroll_x;
12913 scroll_y = old_scroll_y;
12918 ScrollScreen(player, SCROLL_INIT);
12919 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12924 player->StepFrame = 0;
12926 if (moved & MP_MOVING)
12928 if (old_jx != jx && old_jy == jy)
12929 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12930 else if (old_jx == jx && old_jy != jy)
12931 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12933 DrawLevelField(jx, jy); /* for "crumbled sand" */
12935 player->last_move_dir = player->MovDir;
12936 player->is_moving = TRUE;
12937 player->is_snapping = FALSE;
12938 player->is_switching = FALSE;
12939 player->is_dropping = FALSE;
12940 player->is_dropping_pressed = FALSE;
12941 player->drop_pressed_delay = 0;
12944 /* should better be called here than above, but this breaks some tapes */
12945 ScrollPlayer(player, SCROLL_INIT);
12950 CheckGravityMovementWhenNotMoving(player);
12952 player->is_moving = FALSE;
12954 /* at this point, the player is allowed to move, but cannot move right now
12955 (e.g. because of something blocking the way) -- ensure that the player
12956 is also allowed to move in the next frame (in old versions before 3.1.1,
12957 the player was forced to wait again for eight frames before next try) */
12959 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12960 player->move_delay = 0; /* allow direct movement in the next frame */
12963 if (player->move_delay == -1) /* not yet initialized by DigField() */
12964 player->move_delay = player->move_delay_value;
12966 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12968 TestIfPlayerTouchesBadThing(jx, jy);
12969 TestIfPlayerTouchesCustomElement(jx, jy);
12972 if (!player->active)
12973 RemovePlayer(player);
12978 void ScrollPlayer(struct PlayerInfo *player, int mode)
12980 int jx = player->jx, jy = player->jy;
12981 int last_jx = player->last_jx, last_jy = player->last_jy;
12982 int move_stepsize = TILEX / player->move_delay_value;
12984 #if USE_NEW_PLAYER_SPEED
12985 if (!player->active)
12988 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
12991 if (!player->active || player->MovPos == 0)
12995 if (mode == SCROLL_INIT)
12997 player->actual_frame_counter = FrameCounter;
12998 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13000 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13001 Feld[last_jx][last_jy] == EL_EMPTY)
13003 int last_field_block_delay = 0; /* start with no blocking at all */
13004 int block_delay_adjustment = player->block_delay_adjustment;
13006 /* if player blocks last field, add delay for exactly one move */
13007 if (player->block_last_field)
13009 last_field_block_delay += player->move_delay_value;
13011 /* when blocking enabled, prevent moving up despite gravity */
13012 #if USE_PLAYER_GRAVITY
13013 if (player->gravity && player->MovDir == MV_UP)
13014 block_delay_adjustment = -1;
13016 if (game.gravity && player->MovDir == MV_UP)
13017 block_delay_adjustment = -1;
13021 /* add block delay adjustment (also possible when not blocking) */
13022 last_field_block_delay += block_delay_adjustment;
13024 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13025 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13028 #if USE_NEW_PLAYER_SPEED
13029 if (player->MovPos != 0) /* player has not yet reached destination */
13035 else if (!FrameReached(&player->actual_frame_counter, 1))
13038 #if USE_NEW_PLAYER_SPEED
13039 if (player->MovPos != 0)
13041 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13042 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13044 /* before DrawPlayer() to draw correct player graphic for this case */
13045 if (player->MovPos == 0)
13046 CheckGravityMovement(player);
13049 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13050 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13052 /* before DrawPlayer() to draw correct player graphic for this case */
13053 if (player->MovPos == 0)
13054 CheckGravityMovement(player);
13057 if (player->MovPos == 0) /* player reached destination field */
13059 if (player->move_delay_reset_counter > 0)
13061 player->move_delay_reset_counter--;
13063 if (player->move_delay_reset_counter == 0)
13065 /* continue with normal speed after quickly moving through gate */
13066 HALVE_PLAYER_SPEED(player);
13068 /* be able to make the next move without delay */
13069 player->move_delay = 0;
13073 player->last_jx = jx;
13074 player->last_jy = jy;
13076 if (Feld[jx][jy] == EL_EXIT_OPEN ||
13077 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13078 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13079 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13080 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13081 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
13083 DrawPlayer(player); /* needed here only to cleanup last field */
13084 RemovePlayer(player);
13086 if (local_player->friends_still_needed == 0 ||
13087 IS_SP_ELEMENT(Feld[jx][jy]))
13088 PlayerWins(player);
13091 /* this breaks one level: "machine", level 000 */
13093 int move_direction = player->MovDir;
13094 int enter_side = MV_DIR_OPPOSITE(move_direction);
13095 int leave_side = move_direction;
13096 int old_jx = last_jx;
13097 int old_jy = last_jy;
13098 int old_element = Feld[old_jx][old_jy];
13099 int new_element = Feld[jx][jy];
13101 if (IS_CUSTOM_ELEMENT(old_element))
13102 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13104 player->index_bit, leave_side);
13106 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13107 CE_PLAYER_LEAVES_X,
13108 player->index_bit, leave_side);
13110 if (IS_CUSTOM_ELEMENT(new_element))
13111 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13112 player->index_bit, enter_side);
13114 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13115 CE_PLAYER_ENTERS_X,
13116 player->index_bit, enter_side);
13118 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13119 CE_MOVE_OF_X, move_direction);
13122 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13124 TestIfPlayerTouchesBadThing(jx, jy);
13125 TestIfPlayerTouchesCustomElement(jx, jy);
13127 /* needed because pushed element has not yet reached its destination,
13128 so it would trigger a change event at its previous field location */
13129 if (!player->is_pushing)
13130 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
13132 if (!player->active)
13133 RemovePlayer(player);
13136 if (!local_player->LevelSolved && level.use_step_counter)
13146 if (TimeLeft <= 10 && setup.time_limit)
13147 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13150 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13152 DisplayGameControlValues();
13154 DrawGameValue_Time(TimeLeft);
13157 if (!TimeLeft && setup.time_limit)
13158 for (i = 0; i < MAX_PLAYERS; i++)
13159 KillPlayer(&stored_player[i]);
13162 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13164 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13166 DisplayGameControlValues();
13169 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13170 DrawGameValue_Time(TimePlayed);
13174 if (tape.single_step && tape.recording && !tape.pausing &&
13175 !player->programmed_action)
13176 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13180 void ScrollScreen(struct PlayerInfo *player, int mode)
13182 static unsigned long screen_frame_counter = 0;
13184 if (mode == SCROLL_INIT)
13186 /* set scrolling step size according to actual player's moving speed */
13187 ScrollStepSize = TILEX / player->move_delay_value;
13189 screen_frame_counter = FrameCounter;
13190 ScreenMovDir = player->MovDir;
13191 ScreenMovPos = player->MovPos;
13192 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13195 else if (!FrameReached(&screen_frame_counter, 1))
13200 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13201 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13202 redraw_mask |= REDRAW_FIELD;
13205 ScreenMovDir = MV_NONE;
13208 void TestIfPlayerTouchesCustomElement(int x, int y)
13210 static int xy[4][2] =
13217 static int trigger_sides[4][2] =
13219 /* center side border side */
13220 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13221 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13222 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13223 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13225 static int touch_dir[4] =
13227 MV_LEFT | MV_RIGHT,
13232 int center_element = Feld[x][y]; /* should always be non-moving! */
13235 for (i = 0; i < NUM_DIRECTIONS; i++)
13237 int xx = x + xy[i][0];
13238 int yy = y + xy[i][1];
13239 int center_side = trigger_sides[i][0];
13240 int border_side = trigger_sides[i][1];
13241 int border_element;
13243 if (!IN_LEV_FIELD(xx, yy))
13246 if (IS_PLAYER(x, y))
13248 struct PlayerInfo *player = PLAYERINFO(x, y);
13250 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13251 border_element = Feld[xx][yy]; /* may be moving! */
13252 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13253 border_element = Feld[xx][yy];
13254 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13255 border_element = MovingOrBlocked2Element(xx, yy);
13257 continue; /* center and border element do not touch */
13259 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13260 player->index_bit, border_side);
13261 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13262 CE_PLAYER_TOUCHES_X,
13263 player->index_bit, border_side);
13265 else if (IS_PLAYER(xx, yy))
13267 struct PlayerInfo *player = PLAYERINFO(xx, yy);
13269 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13271 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13272 continue; /* center and border element do not touch */
13275 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13276 player->index_bit, center_side);
13277 CheckTriggeredElementChangeByPlayer(x, y, center_element,
13278 CE_PLAYER_TOUCHES_X,
13279 player->index_bit, center_side);
13285 #if USE_ELEMENT_TOUCHING_BUGFIX
13287 void TestIfElementTouchesCustomElement(int x, int y)
13289 static int xy[4][2] =
13296 static int trigger_sides[4][2] =
13298 /* center side border side */
13299 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13300 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13301 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13302 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13304 static int touch_dir[4] =
13306 MV_LEFT | MV_RIGHT,
13311 boolean change_center_element = FALSE;
13312 int center_element = Feld[x][y]; /* should always be non-moving! */
13313 int border_element_old[NUM_DIRECTIONS];
13316 for (i = 0; i < NUM_DIRECTIONS; i++)
13318 int xx = x + xy[i][0];
13319 int yy = y + xy[i][1];
13320 int border_element;
13322 border_element_old[i] = -1;
13324 if (!IN_LEV_FIELD(xx, yy))
13327 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13328 border_element = Feld[xx][yy]; /* may be moving! */
13329 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13330 border_element = Feld[xx][yy];
13331 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13332 border_element = MovingOrBlocked2Element(xx, yy);
13334 continue; /* center and border element do not touch */
13336 border_element_old[i] = border_element;
13339 for (i = 0; i < NUM_DIRECTIONS; i++)
13341 int xx = x + xy[i][0];
13342 int yy = y + xy[i][1];
13343 int center_side = trigger_sides[i][0];
13344 int border_element = border_element_old[i];
13346 if (border_element == -1)
13349 /* check for change of border element */
13350 CheckElementChangeBySide(xx, yy, border_element, center_element,
13351 CE_TOUCHING_X, center_side);
13354 for (i = 0; i < NUM_DIRECTIONS; i++)
13356 int border_side = trigger_sides[i][1];
13357 int border_element = border_element_old[i];
13359 if (border_element == -1)
13362 /* check for change of center element (but change it only once) */
13363 if (!change_center_element)
13364 change_center_element =
13365 CheckElementChangeBySide(x, y, center_element, border_element,
13366 CE_TOUCHING_X, border_side);
13372 void TestIfElementTouchesCustomElement_OLD(int x, int y)
13374 static int xy[4][2] =
13381 static int trigger_sides[4][2] =
13383 /* center side border side */
13384 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13385 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13386 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13387 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13389 static int touch_dir[4] =
13391 MV_LEFT | MV_RIGHT,
13396 boolean change_center_element = FALSE;
13397 int center_element = Feld[x][y]; /* should always be non-moving! */
13400 for (i = 0; i < NUM_DIRECTIONS; i++)
13402 int xx = x + xy[i][0];
13403 int yy = y + xy[i][1];
13404 int center_side = trigger_sides[i][0];
13405 int border_side = trigger_sides[i][1];
13406 int border_element;
13408 if (!IN_LEV_FIELD(xx, yy))
13411 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13412 border_element = Feld[xx][yy]; /* may be moving! */
13413 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13414 border_element = Feld[xx][yy];
13415 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13416 border_element = MovingOrBlocked2Element(xx, yy);
13418 continue; /* center and border element do not touch */
13420 /* check for change of center element (but change it only once) */
13421 if (!change_center_element)
13422 change_center_element =
13423 CheckElementChangeBySide(x, y, center_element, border_element,
13424 CE_TOUCHING_X, border_side);
13426 /* check for change of border element */
13427 CheckElementChangeBySide(xx, yy, border_element, center_element,
13428 CE_TOUCHING_X, center_side);
13434 void TestIfElementHitsCustomElement(int x, int y, int direction)
13436 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13437 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
13438 int hitx = x + dx, hity = y + dy;
13439 int hitting_element = Feld[x][y];
13440 int touched_element;
13442 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13445 touched_element = (IN_LEV_FIELD(hitx, hity) ?
13446 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13448 if (IN_LEV_FIELD(hitx, hity))
13450 int opposite_direction = MV_DIR_OPPOSITE(direction);
13451 int hitting_side = direction;
13452 int touched_side = opposite_direction;
13453 boolean object_hit = (!IS_MOVING(hitx, hity) ||
13454 MovDir[hitx][hity] != direction ||
13455 ABS(MovPos[hitx][hity]) <= TILEY / 2);
13461 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13462 CE_HITTING_X, touched_side);
13464 CheckElementChangeBySide(hitx, hity, touched_element,
13465 hitting_element, CE_HIT_BY_X, hitting_side);
13467 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13468 CE_HIT_BY_SOMETHING, opposite_direction);
13472 /* "hitting something" is also true when hitting the playfield border */
13473 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13474 CE_HITTING_SOMETHING, direction);
13478 void TestIfElementSmashesCustomElement(int x, int y, int direction)
13480 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13481 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
13482 int hitx = x + dx, hity = y + dy;
13483 int hitting_element = Feld[x][y];
13484 int touched_element;
13486 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
13487 !IS_FREE(hitx, hity) &&
13488 (!IS_MOVING(hitx, hity) ||
13489 MovDir[hitx][hity] != direction ||
13490 ABS(MovPos[hitx][hity]) <= TILEY / 2));
13493 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13497 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
13501 touched_element = (IN_LEV_FIELD(hitx, hity) ?
13502 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13504 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13505 EP_CAN_SMASH_EVERYTHING, direction);
13507 if (IN_LEV_FIELD(hitx, hity))
13509 int opposite_direction = MV_DIR_OPPOSITE(direction);
13510 int hitting_side = direction;
13511 int touched_side = opposite_direction;
13513 int touched_element = MovingOrBlocked2Element(hitx, hity);
13516 boolean object_hit = (!IS_MOVING(hitx, hity) ||
13517 MovDir[hitx][hity] != direction ||
13518 ABS(MovPos[hitx][hity]) <= TILEY / 2);
13527 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13528 CE_SMASHED_BY_SOMETHING, opposite_direction);
13530 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13531 CE_OTHER_IS_SMASHING, touched_side);
13533 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13534 CE_OTHER_GETS_SMASHED, hitting_side);
13540 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13542 int i, kill_x = -1, kill_y = -1;
13544 int bad_element = -1;
13545 static int test_xy[4][2] =
13552 static int test_dir[4] =
13560 for (i = 0; i < NUM_DIRECTIONS; i++)
13562 int test_x, test_y, test_move_dir, test_element;
13564 test_x = good_x + test_xy[i][0];
13565 test_y = good_y + test_xy[i][1];
13567 if (!IN_LEV_FIELD(test_x, test_y))
13571 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13573 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13575 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13576 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13578 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13579 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
13583 bad_element = test_element;
13589 if (kill_x != -1 || kill_y != -1)
13591 if (IS_PLAYER(good_x, good_y))
13593 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13595 if (player->shield_deadly_time_left > 0 &&
13596 !IS_INDESTRUCTIBLE(bad_element))
13597 Bang(kill_x, kill_y);
13598 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13599 KillPlayer(player);
13602 Bang(good_x, good_y);
13606 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13608 int i, kill_x = -1, kill_y = -1;
13609 int bad_element = Feld[bad_x][bad_y];
13610 static int test_xy[4][2] =
13617 static int touch_dir[4] =
13619 MV_LEFT | MV_RIGHT,
13624 static int test_dir[4] =
13632 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
13635 for (i = 0; i < NUM_DIRECTIONS; i++)
13637 int test_x, test_y, test_move_dir, test_element;
13639 test_x = bad_x + test_xy[i][0];
13640 test_y = bad_y + test_xy[i][1];
13641 if (!IN_LEV_FIELD(test_x, test_y))
13645 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13647 test_element = Feld[test_x][test_y];
13649 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13650 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13652 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
13653 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
13655 /* good thing is player or penguin that does not move away */
13656 if (IS_PLAYER(test_x, test_y))
13658 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13660 if (bad_element == EL_ROBOT && player->is_moving)
13661 continue; /* robot does not kill player if he is moving */
13663 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13665 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13666 continue; /* center and border element do not touch */
13673 else if (test_element == EL_PENGUIN)
13682 if (kill_x != -1 || kill_y != -1)
13684 if (IS_PLAYER(kill_x, kill_y))
13686 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13688 if (player->shield_deadly_time_left > 0 &&
13689 !IS_INDESTRUCTIBLE(bad_element))
13690 Bang(bad_x, bad_y);
13691 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13692 KillPlayer(player);
13695 Bang(kill_x, kill_y);
13699 void TestIfPlayerTouchesBadThing(int x, int y)
13701 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13704 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13706 TestIfGoodThingHitsBadThing(x, y, move_dir);
13709 void TestIfBadThingTouchesPlayer(int x, int y)
13711 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13714 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13716 TestIfBadThingHitsGoodThing(x, y, move_dir);
13719 void TestIfFriendTouchesBadThing(int x, int y)
13721 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13724 void TestIfBadThingTouchesFriend(int x, int y)
13726 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13729 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13731 int i, kill_x = bad_x, kill_y = bad_y;
13732 static int xy[4][2] =
13740 for (i = 0; i < NUM_DIRECTIONS; i++)
13744 x = bad_x + xy[i][0];
13745 y = bad_y + xy[i][1];
13746 if (!IN_LEV_FIELD(x, y))
13749 element = Feld[x][y];
13750 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13751 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13759 if (kill_x != bad_x || kill_y != bad_y)
13760 Bang(bad_x, bad_y);
13763 void KillPlayer(struct PlayerInfo *player)
13765 int jx = player->jx, jy = player->jy;
13767 if (!player->active)
13770 /* the following code was introduced to prevent an infinite loop when calling
13772 -> CheckTriggeredElementChangeExt()
13773 -> ExecuteCustomElementAction()
13775 -> (infinitely repeating the above sequence of function calls)
13776 which occurs when killing the player while having a CE with the setting
13777 "kill player X when explosion of <player X>"; the solution using a new
13778 field "player->killed" was chosen for backwards compatibility, although
13779 clever use of the fields "player->active" etc. would probably also work */
13781 if (player->killed)
13785 player->killed = TRUE;
13787 /* remove accessible field at the player's position */
13788 Feld[jx][jy] = EL_EMPTY;
13790 /* deactivate shield (else Bang()/Explode() would not work right) */
13791 player->shield_normal_time_left = 0;
13792 player->shield_deadly_time_left = 0;
13795 BuryPlayer(player);
13798 static void KillPlayerUnlessEnemyProtected(int x, int y)
13800 if (!PLAYER_ENEMY_PROTECTED(x, y))
13801 KillPlayer(PLAYERINFO(x, y));
13804 static void KillPlayerUnlessExplosionProtected(int x, int y)
13806 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13807 KillPlayer(PLAYERINFO(x, y));
13810 void BuryPlayer(struct PlayerInfo *player)
13812 int jx = player->jx, jy = player->jy;
13814 if (!player->active)
13817 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13818 PlayLevelSound(jx, jy, SND_GAME_LOSING);
13820 player->GameOver = TRUE;
13821 RemovePlayer(player);
13824 void RemovePlayer(struct PlayerInfo *player)
13826 int jx = player->jx, jy = player->jy;
13827 int i, found = FALSE;
13829 player->present = FALSE;
13830 player->active = FALSE;
13832 if (!ExplodeField[jx][jy])
13833 StorePlayer[jx][jy] = 0;
13835 if (player->is_moving)
13836 DrawLevelField(player->last_jx, player->last_jy);
13838 for (i = 0; i < MAX_PLAYERS; i++)
13839 if (stored_player[i].active)
13843 AllPlayersGone = TRUE;
13849 #if USE_NEW_SNAP_DELAY
13850 static void setFieldForSnapping(int x, int y, int element, int direction)
13852 struct ElementInfo *ei = &element_info[element];
13853 int direction_bit = MV_DIR_TO_BIT(direction);
13854 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13855 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13856 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13858 Feld[x][y] = EL_ELEMENT_SNAPPING;
13859 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13861 ResetGfxAnimation(x, y);
13863 GfxElement[x][y] = element;
13864 GfxAction[x][y] = action;
13865 GfxDir[x][y] = direction;
13866 GfxFrame[x][y] = -1;
13871 =============================================================================
13872 checkDiagonalPushing()
13873 -----------------------------------------------------------------------------
13874 check if diagonal input device direction results in pushing of object
13875 (by checking if the alternative direction is walkable, diggable, ...)
13876 =============================================================================
13879 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13880 int x, int y, int real_dx, int real_dy)
13882 int jx, jy, dx, dy, xx, yy;
13884 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
13887 /* diagonal direction: check alternative direction */
13892 xx = jx + (dx == 0 ? real_dx : 0);
13893 yy = jy + (dy == 0 ? real_dy : 0);
13895 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13899 =============================================================================
13901 -----------------------------------------------------------------------------
13902 x, y: field next to player (non-diagonal) to try to dig to
13903 real_dx, real_dy: direction as read from input device (can be diagonal)
13904 =============================================================================
13907 int DigField(struct PlayerInfo *player,
13908 int oldx, int oldy, int x, int y,
13909 int real_dx, int real_dy, int mode)
13911 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13912 boolean player_was_pushing = player->is_pushing;
13913 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13914 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13915 int jx = oldx, jy = oldy;
13916 int dx = x - jx, dy = y - jy;
13917 int nextx = x + dx, nexty = y + dy;
13918 int move_direction = (dx == -1 ? MV_LEFT :
13919 dx == +1 ? MV_RIGHT :
13921 dy == +1 ? MV_DOWN : MV_NONE);
13922 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13923 int dig_side = MV_DIR_OPPOSITE(move_direction);
13924 int old_element = Feld[jx][jy];
13925 #if USE_FIXED_DONT_RUN_INTO
13926 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13932 if (is_player) /* function can also be called by EL_PENGUIN */
13934 if (player->MovPos == 0)
13936 player->is_digging = FALSE;
13937 player->is_collecting = FALSE;
13940 if (player->MovPos == 0) /* last pushing move finished */
13941 player->is_pushing = FALSE;
13943 if (mode == DF_NO_PUSH) /* player just stopped pushing */
13945 player->is_switching = FALSE;
13946 player->push_delay = -1;
13948 return MP_NO_ACTION;
13952 #if !USE_FIXED_DONT_RUN_INTO
13953 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13954 return MP_NO_ACTION;
13957 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13958 old_element = Back[jx][jy];
13960 /* in case of element dropped at player position, check background */
13961 else if (Back[jx][jy] != EL_EMPTY &&
13962 game.engine_version >= VERSION_IDENT(2,2,0,0))
13963 old_element = Back[jx][jy];
13965 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13966 return MP_NO_ACTION; /* field has no opening in this direction */
13968 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13969 return MP_NO_ACTION; /* field has no opening in this direction */
13971 #if USE_FIXED_DONT_RUN_INTO
13972 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13976 Feld[jx][jy] = player->artwork_element;
13977 InitMovingField(jx, jy, MV_DOWN);
13978 Store[jx][jy] = EL_ACID;
13979 ContinueMoving(jx, jy);
13980 BuryPlayer(player);
13982 return MP_DONT_RUN_INTO;
13986 #if USE_FIXED_DONT_RUN_INTO
13987 if (player_can_move && DONT_RUN_INTO(element))
13989 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13991 return MP_DONT_RUN_INTO;
13995 #if USE_FIXED_DONT_RUN_INTO
13996 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13997 return MP_NO_ACTION;
14000 #if !USE_FIXED_DONT_RUN_INTO
14001 element = Feld[x][y];
14004 collect_count = element_info[element].collect_count_initial;
14006 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
14007 return MP_NO_ACTION;
14009 if (game.engine_version < VERSION_IDENT(2,2,0,0))
14010 player_can_move = player_can_move_or_snap;
14012 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14013 game.engine_version >= VERSION_IDENT(2,2,0,0))
14015 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14016 player->index_bit, dig_side);
14017 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14018 player->index_bit, dig_side);
14020 if (element == EL_DC_LANDMINE)
14023 if (Feld[x][y] != element) /* field changed by snapping */
14026 return MP_NO_ACTION;
14029 #if USE_PLAYER_GRAVITY
14030 if (player->gravity && is_player && !player->is_auto_moving &&
14031 canFallDown(player) && move_direction != MV_DOWN &&
14032 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14033 return MP_NO_ACTION; /* player cannot walk here due to gravity */
14035 if (game.gravity && is_player && !player->is_auto_moving &&
14036 canFallDown(player) && move_direction != MV_DOWN &&
14037 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14038 return MP_NO_ACTION; /* player cannot walk here due to gravity */
14041 if (player_can_move &&
14042 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14044 int sound_element = SND_ELEMENT(element);
14045 int sound_action = ACTION_WALKING;
14047 if (IS_RND_GATE(element))
14049 if (!player->key[RND_GATE_NR(element)])
14050 return MP_NO_ACTION;
14052 else if (IS_RND_GATE_GRAY(element))
14054 if (!player->key[RND_GATE_GRAY_NR(element)])
14055 return MP_NO_ACTION;
14057 else if (IS_RND_GATE_GRAY_ACTIVE(element))
14059 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14060 return MP_NO_ACTION;
14062 else if (element == EL_EXIT_OPEN ||
14063 element == EL_EM_EXIT_OPEN ||
14064 element == EL_STEEL_EXIT_OPEN ||
14065 element == EL_EM_STEEL_EXIT_OPEN ||
14066 element == EL_SP_EXIT_OPEN ||
14067 element == EL_SP_EXIT_OPENING)
14069 sound_action = ACTION_PASSING; /* player is passing exit */
14071 else if (element == EL_EMPTY)
14073 sound_action = ACTION_MOVING; /* nothing to walk on */
14076 /* play sound from background or player, whatever is available */
14077 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14078 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14080 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14082 else if (player_can_move &&
14083 IS_PASSABLE(element) && canPassField(x, y, move_direction))
14085 if (!ACCESS_FROM(element, opposite_direction))
14086 return MP_NO_ACTION; /* field not accessible from this direction */
14088 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
14089 return MP_NO_ACTION;
14091 if (IS_EM_GATE(element))
14093 if (!player->key[EM_GATE_NR(element)])
14094 return MP_NO_ACTION;
14096 else if (IS_EM_GATE_GRAY(element))
14098 if (!player->key[EM_GATE_GRAY_NR(element)])
14099 return MP_NO_ACTION;
14101 else if (IS_EM_GATE_GRAY_ACTIVE(element))
14103 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14104 return MP_NO_ACTION;
14106 else if (IS_EMC_GATE(element))
14108 if (!player->key[EMC_GATE_NR(element)])
14109 return MP_NO_ACTION;
14111 else if (IS_EMC_GATE_GRAY(element))
14113 if (!player->key[EMC_GATE_GRAY_NR(element)])
14114 return MP_NO_ACTION;
14116 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14118 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14119 return MP_NO_ACTION;
14121 else if (element == EL_DC_GATE_WHITE ||
14122 element == EL_DC_GATE_WHITE_GRAY ||
14123 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14125 if (player->num_white_keys == 0)
14126 return MP_NO_ACTION;
14128 player->num_white_keys--;
14130 else if (IS_SP_PORT(element))
14132 if (element == EL_SP_GRAVITY_PORT_LEFT ||
14133 element == EL_SP_GRAVITY_PORT_RIGHT ||
14134 element == EL_SP_GRAVITY_PORT_UP ||
14135 element == EL_SP_GRAVITY_PORT_DOWN)
14136 #if USE_PLAYER_GRAVITY
14137 player->gravity = !player->gravity;
14139 game.gravity = !game.gravity;
14141 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14142 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14143 element == EL_SP_GRAVITY_ON_PORT_UP ||
14144 element == EL_SP_GRAVITY_ON_PORT_DOWN)
14145 #if USE_PLAYER_GRAVITY
14146 player->gravity = TRUE;
14148 game.gravity = TRUE;
14150 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14151 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14152 element == EL_SP_GRAVITY_OFF_PORT_UP ||
14153 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14154 #if USE_PLAYER_GRAVITY
14155 player->gravity = FALSE;
14157 game.gravity = FALSE;
14161 /* automatically move to the next field with double speed */
14162 player->programmed_action = move_direction;
14164 if (player->move_delay_reset_counter == 0)
14166 player->move_delay_reset_counter = 2; /* two double speed steps */
14168 DOUBLE_PLAYER_SPEED(player);
14171 PlayLevelSoundAction(x, y, ACTION_PASSING);
14173 else if (player_can_move_or_snap && IS_DIGGABLE(element))
14177 if (mode != DF_SNAP)
14179 GfxElement[x][y] = GFX_ELEMENT(element);
14180 player->is_digging = TRUE;
14183 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14185 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14186 player->index_bit, dig_side);
14188 if (mode == DF_SNAP)
14190 #if USE_NEW_SNAP_DELAY
14191 if (level.block_snap_field)
14192 setFieldForSnapping(x, y, element, move_direction);
14194 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14196 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14199 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14200 player->index_bit, dig_side);
14203 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14207 if (is_player && mode != DF_SNAP)
14209 GfxElement[x][y] = element;
14210 player->is_collecting = TRUE;
14213 if (element == EL_SPEED_PILL)
14215 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14217 else if (element == EL_EXTRA_TIME && level.time > 0)
14219 TimeLeft += level.extra_time;
14222 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14224 DisplayGameControlValues();
14226 DrawGameValue_Time(TimeLeft);
14229 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14231 player->shield_normal_time_left += level.shield_normal_time;
14232 if (element == EL_SHIELD_DEADLY)
14233 player->shield_deadly_time_left += level.shield_deadly_time;
14235 else if (element == EL_DYNAMITE ||
14236 element == EL_EM_DYNAMITE ||
14237 element == EL_SP_DISK_RED)
14239 if (player->inventory_size < MAX_INVENTORY_SIZE)
14240 player->inventory_element[player->inventory_size++] = element;
14242 DrawGameDoorValues();
14244 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14246 player->dynabomb_count++;
14247 player->dynabombs_left++;
14249 else if (element == EL_DYNABOMB_INCREASE_SIZE)
14251 player->dynabomb_size++;
14253 else if (element == EL_DYNABOMB_INCREASE_POWER)
14255 player->dynabomb_xl = TRUE;
14257 else if (IS_KEY(element))
14259 player->key[KEY_NR(element)] = TRUE;
14261 DrawGameDoorValues();
14263 else if (element == EL_DC_KEY_WHITE)
14265 player->num_white_keys++;
14267 /* display white keys? */
14268 /* DrawGameDoorValues(); */
14270 else if (IS_ENVELOPE(element))
14272 player->show_envelope = element;
14274 else if (element == EL_EMC_LENSES)
14276 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14278 RedrawAllInvisibleElementsForLenses();
14280 else if (element == EL_EMC_MAGNIFIER)
14282 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14284 RedrawAllInvisibleElementsForMagnifier();
14286 else if (IS_DROPPABLE(element) ||
14287 IS_THROWABLE(element)) /* can be collected and dropped */
14291 if (collect_count == 0)
14292 player->inventory_infinite_element = element;
14294 for (i = 0; i < collect_count; i++)
14295 if (player->inventory_size < MAX_INVENTORY_SIZE)
14296 player->inventory_element[player->inventory_size++] = element;
14298 DrawGameDoorValues();
14300 else if (collect_count > 0)
14302 local_player->gems_still_needed -= collect_count;
14303 if (local_player->gems_still_needed < 0)
14304 local_player->gems_still_needed = 0;
14307 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
14309 DisplayGameControlValues();
14311 DrawGameValue_Emeralds(local_player->gems_still_needed);
14315 RaiseScoreElement(element);
14316 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14319 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14320 player->index_bit, dig_side);
14322 if (mode == DF_SNAP)
14324 #if USE_NEW_SNAP_DELAY
14325 if (level.block_snap_field)
14326 setFieldForSnapping(x, y, element, move_direction);
14328 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14330 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14333 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14334 player->index_bit, dig_side);
14337 else if (player_can_move_or_snap && IS_PUSHABLE(element))
14339 if (mode == DF_SNAP && element != EL_BD_ROCK)
14340 return MP_NO_ACTION;
14342 if (CAN_FALL(element) && dy)
14343 return MP_NO_ACTION;
14345 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14346 !(element == EL_SPRING && level.use_spring_bug))
14347 return MP_NO_ACTION;
14349 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14350 ((move_direction & MV_VERTICAL &&
14351 ((element_info[element].move_pattern & MV_LEFT &&
14352 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14353 (element_info[element].move_pattern & MV_RIGHT &&
14354 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14355 (move_direction & MV_HORIZONTAL &&
14356 ((element_info[element].move_pattern & MV_UP &&
14357 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14358 (element_info[element].move_pattern & MV_DOWN &&
14359 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14360 return MP_NO_ACTION;
14362 /* do not push elements already moving away faster than player */
14363 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14364 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14365 return MP_NO_ACTION;
14367 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14369 if (player->push_delay_value == -1 || !player_was_pushing)
14370 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14372 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14374 if (player->push_delay_value == -1)
14375 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14377 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14379 if (!player->is_pushing)
14380 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14383 player->is_pushing = TRUE;
14384 player->is_active = TRUE;
14386 if (!(IN_LEV_FIELD(nextx, nexty) &&
14387 (IS_FREE(nextx, nexty) ||
14388 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
14389 IS_SB_ELEMENT(element)))))
14390 return MP_NO_ACTION;
14392 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14393 return MP_NO_ACTION;
14395 if (player->push_delay == -1) /* new pushing; restart delay */
14396 player->push_delay = 0;
14398 if (player->push_delay < player->push_delay_value &&
14399 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14400 element != EL_SPRING && element != EL_BALLOON)
14402 /* make sure that there is no move delay before next try to push */
14403 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14404 player->move_delay = 0;
14406 return MP_NO_ACTION;
14409 if (IS_SB_ELEMENT(element))
14411 if (element == EL_SOKOBAN_FIELD_FULL)
14413 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14414 local_player->sokobanfields_still_needed++;
14417 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14419 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14420 local_player->sokobanfields_still_needed--;
14423 Feld[x][y] = EL_SOKOBAN_OBJECT;
14425 if (Back[x][y] == Back[nextx][nexty])
14426 PlayLevelSoundAction(x, y, ACTION_PUSHING);
14427 else if (Back[x][y] != 0)
14428 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14431 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14434 if (local_player->sokobanfields_still_needed == 0 &&
14435 game.emulation == EMU_SOKOBAN)
14437 PlayerWins(player);
14439 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14443 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14445 InitMovingField(x, y, move_direction);
14446 GfxAction[x][y] = ACTION_PUSHING;
14448 if (mode == DF_SNAP)
14449 ContinueMoving(x, y);
14451 MovPos[x][y] = (dx != 0 ? dx : dy);
14453 Pushed[x][y] = TRUE;
14454 Pushed[nextx][nexty] = TRUE;
14456 if (game.engine_version < VERSION_IDENT(2,2,0,7))
14457 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14459 player->push_delay_value = -1; /* get new value later */
14461 /* check for element change _after_ element has been pushed */
14462 if (game.use_change_when_pushing_bug)
14464 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14465 player->index_bit, dig_side);
14466 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14467 player->index_bit, dig_side);
14470 else if (IS_SWITCHABLE(element))
14472 if (PLAYER_SWITCHING(player, x, y))
14474 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14475 player->index_bit, dig_side);
14480 player->is_switching = TRUE;
14481 player->switch_x = x;
14482 player->switch_y = y;
14484 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14486 if (element == EL_ROBOT_WHEEL)
14488 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14492 game.robot_wheel_active = TRUE;
14494 DrawLevelField(x, y);
14496 else if (element == EL_SP_TERMINAL)
14500 SCAN_PLAYFIELD(xx, yy)
14502 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14504 else if (Feld[xx][yy] == EL_SP_TERMINAL)
14505 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14508 else if (IS_BELT_SWITCH(element))
14510 ToggleBeltSwitch(x, y);
14512 else if (element == EL_SWITCHGATE_SWITCH_UP ||
14513 element == EL_SWITCHGATE_SWITCH_DOWN ||
14514 element == EL_DC_SWITCHGATE_SWITCH_UP ||
14515 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14517 ToggleSwitchgateSwitch(x, y);
14519 else if (element == EL_LIGHT_SWITCH ||
14520 element == EL_LIGHT_SWITCH_ACTIVE)
14522 ToggleLightSwitch(x, y);
14524 else if (element == EL_TIMEGATE_SWITCH ||
14525 element == EL_DC_TIMEGATE_SWITCH)
14527 ActivateTimegateSwitch(x, y);
14529 else if (element == EL_BALLOON_SWITCH_LEFT ||
14530 element == EL_BALLOON_SWITCH_RIGHT ||
14531 element == EL_BALLOON_SWITCH_UP ||
14532 element == EL_BALLOON_SWITCH_DOWN ||
14533 element == EL_BALLOON_SWITCH_NONE ||
14534 element == EL_BALLOON_SWITCH_ANY)
14536 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
14537 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14538 element == EL_BALLOON_SWITCH_UP ? MV_UP :
14539 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
14540 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
14543 else if (element == EL_LAMP)
14545 Feld[x][y] = EL_LAMP_ACTIVE;
14546 local_player->lights_still_needed--;
14548 ResetGfxAnimation(x, y);
14549 DrawLevelField(x, y);
14551 else if (element == EL_TIME_ORB_FULL)
14553 Feld[x][y] = EL_TIME_ORB_EMPTY;
14555 if (level.time > 0 || level.use_time_orb_bug)
14557 TimeLeft += level.time_orb_time;
14560 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14562 DisplayGameControlValues();
14564 DrawGameValue_Time(TimeLeft);
14568 ResetGfxAnimation(x, y);
14569 DrawLevelField(x, y);
14571 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14572 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14576 game.ball_state = !game.ball_state;
14578 SCAN_PLAYFIELD(xx, yy)
14580 int e = Feld[xx][yy];
14582 if (game.ball_state)
14584 if (e == EL_EMC_MAGIC_BALL)
14585 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14586 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14587 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14591 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14592 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14593 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14594 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14599 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14600 player->index_bit, dig_side);
14602 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14603 player->index_bit, dig_side);
14605 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14606 player->index_bit, dig_side);
14612 if (!PLAYER_SWITCHING(player, x, y))
14614 player->is_switching = TRUE;
14615 player->switch_x = x;
14616 player->switch_y = y;
14618 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14619 player->index_bit, dig_side);
14620 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14621 player->index_bit, dig_side);
14623 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14624 player->index_bit, dig_side);
14625 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14626 player->index_bit, dig_side);
14629 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14630 player->index_bit, dig_side);
14631 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14632 player->index_bit, dig_side);
14634 return MP_NO_ACTION;
14637 player->push_delay = -1;
14639 if (is_player) /* function can also be called by EL_PENGUIN */
14641 if (Feld[x][y] != element) /* really digged/collected something */
14643 player->is_collecting = !player->is_digging;
14644 player->is_active = TRUE;
14651 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14653 int jx = player->jx, jy = player->jy;
14654 int x = jx + dx, y = jy + dy;
14655 int snap_direction = (dx == -1 ? MV_LEFT :
14656 dx == +1 ? MV_RIGHT :
14658 dy == +1 ? MV_DOWN : MV_NONE);
14659 boolean can_continue_snapping = (level.continuous_snapping &&
14660 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14662 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14665 if (!player->active || !IN_LEV_FIELD(x, y))
14673 if (player->MovPos == 0)
14674 player->is_pushing = FALSE;
14676 player->is_snapping = FALSE;
14678 if (player->MovPos == 0)
14680 player->is_moving = FALSE;
14681 player->is_digging = FALSE;
14682 player->is_collecting = FALSE;
14688 #if USE_NEW_CONTINUOUS_SNAPPING
14689 /* prevent snapping with already pressed snap key when not allowed */
14690 if (player->is_snapping && !can_continue_snapping)
14693 if (player->is_snapping)
14697 player->MovDir = snap_direction;
14699 if (player->MovPos == 0)
14701 player->is_moving = FALSE;
14702 player->is_digging = FALSE;
14703 player->is_collecting = FALSE;
14706 player->is_dropping = FALSE;
14707 player->is_dropping_pressed = FALSE;
14708 player->drop_pressed_delay = 0;
14710 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14713 player->is_snapping = TRUE;
14714 player->is_active = TRUE;
14716 if (player->MovPos == 0)
14718 player->is_moving = FALSE;
14719 player->is_digging = FALSE;
14720 player->is_collecting = FALSE;
14723 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
14724 DrawLevelField(player->last_jx, player->last_jy);
14726 DrawLevelField(x, y);
14731 boolean DropElement(struct PlayerInfo *player)
14733 int old_element, new_element;
14734 int dropx = player->jx, dropy = player->jy;
14735 int drop_direction = player->MovDir;
14736 int drop_side = drop_direction;
14738 int drop_element = get_next_dropped_element(player);
14740 int drop_element = (player->inventory_size > 0 ?
14741 player->inventory_element[player->inventory_size - 1] :
14742 player->inventory_infinite_element != EL_UNDEFINED ?
14743 player->inventory_infinite_element :
14744 player->dynabombs_left > 0 ?
14745 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
14749 player->is_dropping_pressed = TRUE;
14751 /* do not drop an element on top of another element; when holding drop key
14752 pressed without moving, dropped element must move away before the next
14753 element can be dropped (this is especially important if the next element
14754 is dynamite, which can be placed on background for historical reasons) */
14755 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14758 if (IS_THROWABLE(drop_element))
14760 dropx += GET_DX_FROM_DIR(drop_direction);
14761 dropy += GET_DY_FROM_DIR(drop_direction);
14763 if (!IN_LEV_FIELD(dropx, dropy))
14767 old_element = Feld[dropx][dropy]; /* old element at dropping position */
14768 new_element = drop_element; /* default: no change when dropping */
14770 /* check if player is active, not moving and ready to drop */
14771 if (!player->active || player->MovPos || player->drop_delay > 0)
14774 /* check if player has anything that can be dropped */
14775 if (new_element == EL_UNDEFINED)
14778 /* check if drop key was pressed long enough for EM style dynamite */
14779 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14782 /* check if anything can be dropped at the current position */
14783 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14786 /* collected custom elements can only be dropped on empty fields */
14787 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14790 if (old_element != EL_EMPTY)
14791 Back[dropx][dropy] = old_element; /* store old element on this field */
14793 ResetGfxAnimation(dropx, dropy);
14794 ResetRandomAnimationValue(dropx, dropy);
14796 if (player->inventory_size > 0 ||
14797 player->inventory_infinite_element != EL_UNDEFINED)
14799 if (player->inventory_size > 0)
14801 player->inventory_size--;
14803 DrawGameDoorValues();
14805 if (new_element == EL_DYNAMITE)
14806 new_element = EL_DYNAMITE_ACTIVE;
14807 else if (new_element == EL_EM_DYNAMITE)
14808 new_element = EL_EM_DYNAMITE_ACTIVE;
14809 else if (new_element == EL_SP_DISK_RED)
14810 new_element = EL_SP_DISK_RED_ACTIVE;
14813 Feld[dropx][dropy] = new_element;
14815 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14816 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14817 el2img(Feld[dropx][dropy]), 0);
14819 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14821 /* needed if previous element just changed to "empty" in the last frame */
14822 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14824 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14825 player->index_bit, drop_side);
14826 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14828 player->index_bit, drop_side);
14830 TestIfElementTouchesCustomElement(dropx, dropy);
14832 else /* player is dropping a dyna bomb */
14834 player->dynabombs_left--;
14836 Feld[dropx][dropy] = new_element;
14838 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14839 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14840 el2img(Feld[dropx][dropy]), 0);
14842 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14845 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14846 InitField_WithBug1(dropx, dropy, FALSE);
14848 new_element = Feld[dropx][dropy]; /* element might have changed */
14850 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14851 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14853 int move_direction, nextx, nexty;
14855 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14856 MovDir[dropx][dropy] = drop_direction;
14858 move_direction = MovDir[dropx][dropy];
14859 nextx = dropx + GET_DX_FROM_DIR(move_direction);
14860 nexty = dropy + GET_DY_FROM_DIR(move_direction);
14862 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14864 #if USE_FIX_IMPACT_COLLISION
14865 /* do not cause impact style collision by dropping elements that can fall */
14866 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14868 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14872 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14873 player->is_dropping = TRUE;
14875 player->drop_pressed_delay = 0;
14876 player->is_dropping_pressed = FALSE;
14878 player->drop_x = dropx;
14879 player->drop_y = dropy;
14884 /* ------------------------------------------------------------------------- */
14885 /* game sound playing functions */
14886 /* ------------------------------------------------------------------------- */
14888 static int *loop_sound_frame = NULL;
14889 static int *loop_sound_volume = NULL;
14891 void InitPlayLevelSound()
14893 int num_sounds = getSoundListSize();
14895 checked_free(loop_sound_frame);
14896 checked_free(loop_sound_volume);
14898 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
14899 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14902 static void PlayLevelSound(int x, int y, int nr)
14904 int sx = SCREENX(x), sy = SCREENY(y);
14905 int volume, stereo_position;
14906 int max_distance = 8;
14907 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14909 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14910 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14913 if (!IN_LEV_FIELD(x, y) ||
14914 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14915 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14918 volume = SOUND_MAX_VOLUME;
14920 if (!IN_SCR_FIELD(sx, sy))
14922 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14923 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14925 volume -= volume * (dx > dy ? dx : dy) / max_distance;
14928 stereo_position = (SOUND_MAX_LEFT +
14929 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14930 (SCR_FIELDX + 2 * max_distance));
14932 if (IS_LOOP_SOUND(nr))
14934 /* This assures that quieter loop sounds do not overwrite louder ones,
14935 while restarting sound volume comparison with each new game frame. */
14937 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14940 loop_sound_volume[nr] = volume;
14941 loop_sound_frame[nr] = FrameCounter;
14944 PlaySoundExt(nr, volume, stereo_position, type);
14947 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14949 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14950 x > LEVELX(BX2) ? LEVELX(BX2) : x,
14951 y < LEVELY(BY1) ? LEVELY(BY1) :
14952 y > LEVELY(BY2) ? LEVELY(BY2) : y,
14956 static void PlayLevelSoundAction(int x, int y, int action)
14958 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14961 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14963 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14965 if (sound_effect != SND_UNDEFINED)
14966 PlayLevelSound(x, y, sound_effect);
14969 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14972 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14974 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14975 PlayLevelSound(x, y, sound_effect);
14978 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14980 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14982 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14983 PlayLevelSound(x, y, sound_effect);
14986 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14988 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14990 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14991 StopSound(sound_effect);
14994 static void PlayLevelMusic()
14996 if (levelset.music[level_nr] != MUS_UNDEFINED)
14997 PlayMusic(levelset.music[level_nr]); /* from config file */
14999 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
15002 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15004 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
15005 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
15006 int x = xx - 1 - offset;
15007 int y = yy - 1 - offset;
15012 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15016 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15020 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15024 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15028 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15032 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15036 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15039 case SAMPLE_android_clone:
15040 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15043 case SAMPLE_android_move:
15044 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15047 case SAMPLE_spring:
15048 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15052 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15056 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15059 case SAMPLE_eater_eat:
15060 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15064 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15067 case SAMPLE_collect:
15068 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15071 case SAMPLE_diamond:
15072 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15075 case SAMPLE_squash:
15076 /* !!! CHECK THIS !!! */
15078 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15080 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15084 case SAMPLE_wonderfall:
15085 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15089 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15093 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15097 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15101 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15105 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15109 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15112 case SAMPLE_wonder:
15113 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15117 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15120 case SAMPLE_exit_open:
15121 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15124 case SAMPLE_exit_leave:
15125 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15128 case SAMPLE_dynamite:
15129 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15133 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15137 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15141 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15145 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15149 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15153 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15157 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15163 void ChangeTime(int value)
15165 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
15169 /* EMC game engine uses value from time counter of RND game engine */
15170 level.native_em_level->lev->time = *time;
15172 DrawGameValue_Time(*time);
15175 void RaiseScore(int value)
15177 /* EMC game engine and RND game engine have separate score counters */
15178 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
15179 &level.native_em_level->lev->score : &local_player->score);
15183 DrawGameValue_Score(*score);
15187 void RaiseScore(int value)
15189 local_player->score += value;
15192 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
15194 DisplayGameControlValues();
15196 DrawGameValue_Score(local_player->score);
15200 void RaiseScoreElement(int element)
15205 case EL_BD_DIAMOND:
15206 case EL_EMERALD_YELLOW:
15207 case EL_EMERALD_RED:
15208 case EL_EMERALD_PURPLE:
15209 case EL_SP_INFOTRON:
15210 RaiseScore(level.score[SC_EMERALD]);
15213 RaiseScore(level.score[SC_DIAMOND]);
15216 RaiseScore(level.score[SC_CRYSTAL]);
15219 RaiseScore(level.score[SC_PEARL]);
15222 case EL_BD_BUTTERFLY:
15223 case EL_SP_ELECTRON:
15224 RaiseScore(level.score[SC_BUG]);
15227 case EL_BD_FIREFLY:
15228 case EL_SP_SNIKSNAK:
15229 RaiseScore(level.score[SC_SPACESHIP]);
15232 case EL_DARK_YAMYAM:
15233 RaiseScore(level.score[SC_YAMYAM]);
15236 RaiseScore(level.score[SC_ROBOT]);
15239 RaiseScore(level.score[SC_PACMAN]);
15242 RaiseScore(level.score[SC_NUT]);
15245 case EL_EM_DYNAMITE:
15246 case EL_SP_DISK_RED:
15247 case EL_DYNABOMB_INCREASE_NUMBER:
15248 case EL_DYNABOMB_INCREASE_SIZE:
15249 case EL_DYNABOMB_INCREASE_POWER:
15250 RaiseScore(level.score[SC_DYNAMITE]);
15252 case EL_SHIELD_NORMAL:
15253 case EL_SHIELD_DEADLY:
15254 RaiseScore(level.score[SC_SHIELD]);
15256 case EL_EXTRA_TIME:
15257 RaiseScore(level.extra_time_score);
15271 case EL_DC_KEY_WHITE:
15272 RaiseScore(level.score[SC_KEY]);
15275 RaiseScore(element_info[element].collect_score);
15280 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15282 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15284 #if defined(NETWORK_AVALIABLE)
15285 if (options.network)
15286 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15295 FadeSkipNextFadeIn();
15297 fading = fading_none;
15301 OpenDoor(DOOR_CLOSE_1);
15304 game_status = GAME_MODE_MAIN;
15307 DrawAndFadeInMainMenu(REDRAW_FIELD);
15315 FadeOut(REDRAW_FIELD);
15318 game_status = GAME_MODE_MAIN;
15320 DrawAndFadeInMainMenu(REDRAW_FIELD);
15324 else /* continue playing the game */
15326 if (tape.playing && tape.deactivate_display)
15327 TapeDeactivateDisplayOff(TRUE);
15329 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15331 if (tape.playing && tape.deactivate_display)
15332 TapeDeactivateDisplayOn();
15336 void RequestQuitGame(boolean ask_if_really_quit)
15338 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15339 boolean skip_request = AllPlayersGone || quick_quit;
15341 RequestQuitGameExt(skip_request, quick_quit,
15342 "Do you really want to quit the game ?");
15346 /* ------------------------------------------------------------------------- */
15347 /* random generator functions */
15348 /* ------------------------------------------------------------------------- */
15350 unsigned int InitEngineRandom_RND(long seed)
15352 game.num_random_calls = 0;
15355 unsigned int rnd_seed = InitEngineRandom(seed);
15357 printf("::: START RND: %d\n", rnd_seed);
15362 return InitEngineRandom(seed);
15368 unsigned int RND(int max)
15372 game.num_random_calls++;
15374 return GetEngineRandom(max);
15381 /* ------------------------------------------------------------------------- */
15382 /* game engine snapshot handling functions */
15383 /* ------------------------------------------------------------------------- */
15385 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
15387 struct EngineSnapshotInfo
15389 /* runtime values for custom element collect score */
15390 int collect_score[NUM_CUSTOM_ELEMENTS];
15392 /* runtime values for group element choice position */
15393 int choice_pos[NUM_GROUP_ELEMENTS];
15395 /* runtime values for belt position animations */
15396 int belt_graphic[4 * NUM_BELT_PARTS];
15397 int belt_anim_mode[4 * NUM_BELT_PARTS];
15400 struct EngineSnapshotNodeInfo
15407 static struct EngineSnapshotInfo engine_snapshot_rnd;
15408 static ListNode *engine_snapshot_list = NULL;
15409 static char *snapshot_level_identifier = NULL;
15410 static int snapshot_level_nr = -1;
15412 void FreeEngineSnapshot()
15414 while (engine_snapshot_list != NULL)
15415 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
15418 setString(&snapshot_level_identifier, NULL);
15419 snapshot_level_nr = -1;
15422 static void SaveEngineSnapshotValues_RND()
15424 static int belt_base_active_element[4] =
15426 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15427 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15428 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15429 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15433 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15435 int element = EL_CUSTOM_START + i;
15437 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15440 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15442 int element = EL_GROUP_START + i;
15444 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15447 for (i = 0; i < 4; i++)
15449 for (j = 0; j < NUM_BELT_PARTS; j++)
15451 int element = belt_base_active_element[i] + j;
15452 int graphic = el2img(element);
15453 int anim_mode = graphic_info[graphic].anim_mode;
15455 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
15456 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
15461 static void LoadEngineSnapshotValues_RND()
15463 unsigned long num_random_calls = game.num_random_calls;
15466 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15468 int element = EL_CUSTOM_START + i;
15470 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15473 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15475 int element = EL_GROUP_START + i;
15477 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15480 for (i = 0; i < 4; i++)
15482 for (j = 0; j < NUM_BELT_PARTS; j++)
15484 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
15485 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
15487 graphic_info[graphic].anim_mode = anim_mode;
15491 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15493 InitRND(tape.random_seed);
15494 for (i = 0; i < num_random_calls; i++)
15498 if (game.num_random_calls != num_random_calls)
15500 Error(ERR_INFO, "number of random calls out of sync");
15501 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15502 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15503 Error(ERR_EXIT, "this should not happen -- please debug");
15507 static void SaveEngineSnapshotBuffer(void *buffer, int size)
15509 struct EngineSnapshotNodeInfo *bi =
15510 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
15512 bi->buffer_orig = buffer;
15513 bi->buffer_copy = checked_malloc(size);
15516 memcpy(bi->buffer_copy, buffer, size);
15518 addNodeToList(&engine_snapshot_list, NULL, bi);
15521 void SaveEngineSnapshot()
15523 FreeEngineSnapshot(); /* free previous snapshot, if needed */
15525 if (level_editor_test_game) /* do not save snapshots from editor */
15528 /* copy some special values to a structure better suited for the snapshot */
15530 SaveEngineSnapshotValues_RND();
15531 SaveEngineSnapshotValues_EM();
15533 /* save values stored in special snapshot structure */
15535 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15536 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15538 /* save further RND engine values */
15540 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
15541 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
15542 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
15544 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
15545 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
15546 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
15547 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
15549 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15550 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15551 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15552 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15553 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15555 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15556 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15557 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15559 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15561 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15563 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15564 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15566 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
15567 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
15568 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
15569 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15570 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15571 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15572 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15573 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
15574 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
15575 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15576 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
15577 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15578 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15579 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15580 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15581 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15582 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
15583 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
15585 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15586 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15588 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15589 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15590 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15592 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15593 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15595 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15596 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15597 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15598 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15599 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15601 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15602 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15604 /* save level identification information */
15606 setString(&snapshot_level_identifier, leveldir_current->identifier);
15607 snapshot_level_nr = level_nr;
15610 ListNode *node = engine_snapshot_list;
15613 while (node != NULL)
15615 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15620 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15624 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
15626 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
15629 void LoadEngineSnapshot()
15631 ListNode *node = engine_snapshot_list;
15633 if (engine_snapshot_list == NULL)
15636 while (node != NULL)
15638 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
15643 /* restore special values from snapshot structure */
15645 LoadEngineSnapshotValues_RND();
15646 LoadEngineSnapshotValues_EM();
15649 boolean CheckEngineSnapshot()
15651 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15652 snapshot_level_nr == level_nr);
15656 /* ---------- new game button stuff ---------------------------------------- */
15658 /* graphic position values for game buttons */
15659 #define GAME_BUTTON_XSIZE 30
15660 #define GAME_BUTTON_YSIZE 30
15661 #define GAME_BUTTON_XPOS 5
15662 #define GAME_BUTTON_YPOS 215
15663 #define SOUND_BUTTON_XPOS 5
15664 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
15666 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15667 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15668 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15669 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15670 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15671 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15679 } gamebutton_info[NUM_GAME_BUTTONS] =
15683 &game.button.stop.x, &game.button.stop.y,
15684 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
15689 &game.button.pause.x, &game.button.pause.y,
15690 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
15691 GAME_CTRL_ID_PAUSE,
15695 &game.button.play.x, &game.button.play.y,
15696 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
15701 &game.button.sound_music.x, &game.button.sound_music.y,
15702 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
15703 SOUND_CTRL_ID_MUSIC,
15704 "background music on/off"
15707 &game.button.sound_loops.x, &game.button.sound_loops.y,
15708 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
15709 SOUND_CTRL_ID_LOOPS,
15710 "sound loops on/off"
15713 &game.button.sound_simple.x,&game.button.sound_simple.y,
15714 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
15715 SOUND_CTRL_ID_SIMPLE,
15716 "normal sounds on/off"
15720 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
15725 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
15726 GAME_CTRL_ID_PAUSE,
15730 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
15735 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
15736 SOUND_CTRL_ID_MUSIC,
15737 "background music on/off"
15740 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
15741 SOUND_CTRL_ID_LOOPS,
15742 "sound loops on/off"
15745 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
15746 SOUND_CTRL_ID_SIMPLE,
15747 "normal sounds on/off"
15752 void CreateGameButtons()
15756 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15758 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
15759 struct GadgetInfo *gi;
15762 unsigned long event_mask;
15764 int gd_xoffset, gd_yoffset;
15765 int gd_x1, gd_x2, gd_y1, gd_y2;
15768 x = DX + *gamebutton_info[i].x;
15769 y = DY + *gamebutton_info[i].y;
15770 gd_xoffset = gamebutton_info[i].gd_x;
15771 gd_yoffset = gamebutton_info[i].gd_y;
15772 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
15773 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
15775 if (id == GAME_CTRL_ID_STOP ||
15776 id == GAME_CTRL_ID_PAUSE ||
15777 id == GAME_CTRL_ID_PLAY)
15779 button_type = GD_TYPE_NORMAL_BUTTON;
15781 event_mask = GD_EVENT_RELEASED;
15782 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15783 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15787 button_type = GD_TYPE_CHECK_BUTTON;
15789 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15790 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15791 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15792 event_mask = GD_EVENT_PRESSED;
15793 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
15794 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15797 gi = CreateGadget(GDI_CUSTOM_ID, id,
15798 GDI_INFO_TEXT, gamebutton_info[i].infotext,
15803 GDI_X, DX + gd_xoffset,
15804 GDI_Y, DY + gd_yoffset,
15806 GDI_WIDTH, GAME_BUTTON_XSIZE,
15807 GDI_HEIGHT, GAME_BUTTON_YSIZE,
15808 GDI_TYPE, button_type,
15809 GDI_STATE, GD_BUTTON_UNPRESSED,
15810 GDI_CHECKED, checked,
15811 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
15812 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
15813 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
15814 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
15815 GDI_DIRECT_DRAW, FALSE,
15816 GDI_EVENT_MASK, event_mask,
15817 GDI_CALLBACK_ACTION, HandleGameButtons,
15821 Error(ERR_EXIT, "cannot create gadget");
15823 game_gadget[id] = gi;
15827 void FreeGameButtons()
15831 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15832 FreeGadget(game_gadget[i]);
15835 static void MapGameButtons()
15839 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15840 MapGadget(game_gadget[i]);
15843 void UnmapGameButtons()
15847 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15848 UnmapGadget(game_gadget[i]);
15851 void RedrawGameButtons()
15855 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15856 RedrawGadget(game_gadget[i]);
15859 static void HandleGameButtons(struct GadgetInfo *gi)
15861 int id = gi->custom_id;
15863 if (game_status != GAME_MODE_PLAYING)
15868 case GAME_CTRL_ID_STOP:
15872 RequestQuitGame(TRUE);
15875 case GAME_CTRL_ID_PAUSE:
15876 if (options.network)
15878 #if defined(NETWORK_AVALIABLE)
15880 SendToServer_ContinuePlaying();
15882 SendToServer_PausePlaying();
15886 TapeTogglePause(TAPE_TOGGLE_MANUAL);
15889 case GAME_CTRL_ID_PLAY:
15892 #if defined(NETWORK_AVALIABLE)
15893 if (options.network)
15894 SendToServer_ContinuePlaying();
15898 tape.pausing = FALSE;
15899 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
15904 case SOUND_CTRL_ID_MUSIC:
15905 if (setup.sound_music)
15907 setup.sound_music = FALSE;
15910 else if (audio.music_available)
15912 setup.sound = setup.sound_music = TRUE;
15914 SetAudioMode(setup.sound);
15920 case SOUND_CTRL_ID_LOOPS:
15921 if (setup.sound_loops)
15922 setup.sound_loops = FALSE;
15923 else if (audio.loops_available)
15925 setup.sound = setup.sound_loops = TRUE;
15926 SetAudioMode(setup.sound);
15930 case SOUND_CTRL_ID_SIMPLE:
15931 if (setup.sound_simple)
15932 setup.sound_simple = FALSE;
15933 else if (audio.sound_available)
15935 setup.sound = setup.sound_simple = TRUE;
15936 SetAudioMode(setup.sound);