1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include "libgame/libgame.h"
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF ( 1)
30 #define USE_NEW_SP_SLIPPERY (USE_NEW_STUFF * 1)
31 #define USE_NEW_CUSTOM_VALUE (USE_NEW_STUFF * 1)
32 #define USE_NEW_PLAYER_ANIM (USE_NEW_STUFF * 1)
33 #define USE_NEW_ALL_SLIPPERY (USE_NEW_STUFF * 1)
34 #define USE_NEW_PLAYER_SPEED (USE_NEW_STUFF * 1)
35 #define USE_NEW_DELAYED_ACTION (USE_NEW_STUFF * 1)
36 #define USE_NEW_SNAP_DELAY (USE_NEW_STUFF * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
39 #define USE_FIXED_DONT_RUN_INTO (USE_NEW_STUFF * 1)
40 #define USE_NEW_SPRING_BUMPER (USE_NEW_STUFF * 1)
41 #define USE_STOP_CHANGED_ELEMENTS (USE_NEW_STUFF * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX (USE_NEW_STUFF * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING (USE_NEW_STUFF * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION (USE_NEW_STUFF * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES (USE_NEW_STUFF * 1)
46 #define USE_PLAYER_GRAVITY (USE_NEW_STUFF * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX (USE_NEW_STUFF * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX (USE_NEW_STUFF * 0)
50 #define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0)
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF * 1)
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX (USE_NEW_STUFF * 1)
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING (USE_NEW_STUFF * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK (USE_NEW_STUFF * 1)
59 #define USE_FIX_KILLED_BY_NON_WALKABLE (USE_NEW_STUFF * 1)
60 #define USE_FIX_IMPACT_COLLISION (USE_NEW_STUFF * 1)
62 #define USE_GFX_RESET_WHEN_NOT_MOVING (USE_NEW_STUFF * 1)
70 /* for MovePlayer() */
71 #define MP_NO_ACTION 0
74 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
76 /* for ScrollPlayer() */
78 #define SCROLL_GO_ON 1
80 /* for Bang()/Explode() */
81 #define EX_PHASE_START 0
82 #define EX_TYPE_NONE 0
83 #define EX_TYPE_NORMAL (1 << 0)
84 #define EX_TYPE_CENTER (1 << 1)
85 #define EX_TYPE_BORDER (1 << 2)
86 #define EX_TYPE_CROSS (1 << 3)
87 #define EX_TYPE_DYNA (1 << 4)
88 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
90 #define PANEL_OFF() (local_player->LevelSolved_PanelOff)
91 #define PANEL_DEACTIVATED(p) ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
92 #define PANEL_XPOS(p) (DX + ALIGNED_TEXT_XPOS(p))
93 #define PANEL_YPOS(p) (DY + ALIGNED_TEXT_YPOS(p))
95 /* special positions in the game control window (relative to control window) */
96 #define XX_LEVEL1 (PANEL_XPOS(game.panel.level))
97 #define XX_LEVEL2 (PANEL_XPOS(game.panel.level) - 1)
98 #define XX_LEVEL (PANEL_XPOS(game.panel.level))
99 #define YY_LEVEL (PANEL_YPOS(game.panel.level))
100 #define XX_EMERALDS (PANEL_XPOS(game.panel.gems))
101 #define YY_EMERALDS (PANEL_YPOS(game.panel.gems))
102 #define XX_DYNAMITE (PANEL_XPOS(game.panel.inventory))
103 #define YY_DYNAMITE (PANEL_YPOS(game.panel.inventory))
104 #define XX_KEYS (PANEL_XPOS(game.panel.keys))
105 #define YY_KEYS (PANEL_YPOS(game.panel.keys))
106 #define XX_SCORE (PANEL_XPOS(game.panel.score))
107 #define YY_SCORE (PANEL_YPOS(game.panel.score))
108 #define XX_TIME1 (PANEL_XPOS(game.panel.time))
109 #define XX_TIME2 (PANEL_XPOS(game.panel.time) + 1)
110 #define XX_TIME (PANEL_XPOS(game.panel.time))
111 #define YY_TIME (PANEL_YPOS(game.panel.time))
113 /* special positions in the game control window (relative to main window) */
114 #define DX_LEVEL1 (DX + XX_LEVEL1)
115 #define DX_LEVEL2 (DX + XX_LEVEL2)
116 #define DX_LEVEL (DX + XX_LEVEL)
117 #define DY_LEVEL (DY + YY_LEVEL)
118 #define DX_EMERALDS (DX + XX_EMERALDS)
119 #define DY_EMERALDS (DY + YY_EMERALDS)
120 #define DX_DYNAMITE (DX + XX_DYNAMITE)
121 #define DY_DYNAMITE (DY + YY_DYNAMITE)
122 #define DX_KEYS (DX + XX_KEYS)
123 #define DY_KEYS (DY + YY_KEYS)
124 #define DX_SCORE (DX + XX_SCORE)
125 #define DY_SCORE (DY + YY_SCORE)
126 #define DX_TIME1 (DX + XX_TIME1)
127 #define DX_TIME2 (DX + XX_TIME2)
128 #define DX_TIME (DX + XX_TIME)
129 #define DY_TIME (DY + YY_TIME)
132 /* game panel display and control definitions */
134 #define GAME_PANEL_LEVEL_NUMBER 0
135 #define GAME_PANEL_GEMS 1
136 #define GAME_PANEL_INVENTORY_COUNT 2
137 #define GAME_PANEL_INVENTORY_FIRST_1 3
138 #define GAME_PANEL_INVENTORY_FIRST_2 4
139 #define GAME_PANEL_INVENTORY_FIRST_3 5
140 #define GAME_PANEL_INVENTORY_FIRST_4 6
141 #define GAME_PANEL_INVENTORY_FIRST_5 7
142 #define GAME_PANEL_INVENTORY_FIRST_6 8
143 #define GAME_PANEL_INVENTORY_FIRST_7 9
144 #define GAME_PANEL_INVENTORY_FIRST_8 10
145 #define GAME_PANEL_INVENTORY_LAST_1 11
146 #define GAME_PANEL_INVENTORY_LAST_2 12
147 #define GAME_PANEL_INVENTORY_LAST_3 13
148 #define GAME_PANEL_INVENTORY_LAST_4 14
149 #define GAME_PANEL_INVENTORY_LAST_5 15
150 #define GAME_PANEL_INVENTORY_LAST_6 16
151 #define GAME_PANEL_INVENTORY_LAST_7 17
152 #define GAME_PANEL_INVENTORY_LAST_8 18
153 #define GAME_PANEL_KEY_1 19
154 #define GAME_PANEL_KEY_2 20
155 #define GAME_PANEL_KEY_3 21
156 #define GAME_PANEL_KEY_4 22
157 #define GAME_PANEL_KEY_5 23
158 #define GAME_PANEL_KEY_6 24
159 #define GAME_PANEL_KEY_7 25
160 #define GAME_PANEL_KEY_8 26
161 #define GAME_PANEL_KEY_WHITE 27
162 #define GAME_PANEL_KEY_WHITE_COUNT 28
163 #define GAME_PANEL_SCORE 29
164 #define GAME_PANEL_TIME 30
165 #define GAME_PANEL_TIME_HH 31
166 #define GAME_PANEL_TIME_MM 32
167 #define GAME_PANEL_TIME_SS 33
168 #define GAME_PANEL_SHIELD_NORMAL 34
169 #define GAME_PANEL_SHIELD_NORMAL_TIME 35
170 #define GAME_PANEL_SHIELD_DEADLY 36
171 #define GAME_PANEL_SHIELD_DEADLY_TIME 37
172 #define GAME_PANEL_EXIT 38
173 #define GAME_PANEL_EMC_MAGIC_BALL 39
174 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH 40
175 #define GAME_PANEL_LIGHT_SWITCH 41
176 #define GAME_PANEL_LIGHT_SWITCH_TIME 42
177 #define GAME_PANEL_TIMEGATE_SWITCH 43
178 #define GAME_PANEL_TIMEGATE_SWITCH_TIME 44
179 #define GAME_PANEL_SWITCHGATE_SWITCH 45
180 #define GAME_PANEL_EMC_LENSES 46
181 #define GAME_PANEL_EMC_LENSES_TIME 47
182 #define GAME_PANEL_EMC_MAGNIFIER 48
183 #define GAME_PANEL_EMC_MAGNIFIER_TIME 49
184 #define GAME_PANEL_BALLOON_SWITCH 50
185 #define GAME_PANEL_DYNABOMB_NUMBER 51
186 #define GAME_PANEL_DYNABOMB_SIZE 52
187 #define GAME_PANEL_DYNABOMB_POWER 53
188 #define GAME_PANEL_PENGUINS 54
189 #define GAME_PANEL_SOKOBAN_OBJECTS 55
190 #define GAME_PANEL_SOKOBAN_FIELDS 56
191 #define GAME_PANEL_ROBOT_WHEEL 57
192 #define GAME_PANEL_CONVEYOR_BELT_1 58
193 #define GAME_PANEL_CONVEYOR_BELT_2 59
194 #define GAME_PANEL_CONVEYOR_BELT_3 60
195 #define GAME_PANEL_CONVEYOR_BELT_4 61
196 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH 62
197 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH 63
198 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH 64
199 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH 65
200 #define GAME_PANEL_MAGIC_WALL 66
201 #define GAME_PANEL_MAGIC_WALL_TIME 67
202 #define GAME_PANEL_GRAVITY_STATE 68
203 #define GAME_PANEL_GRAPHIC_1 69
204 #define GAME_PANEL_GRAPHIC_2 70
205 #define GAME_PANEL_GRAPHIC_3 71
206 #define GAME_PANEL_GRAPHIC_4 72
207 #define GAME_PANEL_GRAPHIC_5 73
208 #define GAME_PANEL_GRAPHIC_6 74
209 #define GAME_PANEL_GRAPHIC_7 75
210 #define GAME_PANEL_GRAPHIC_8 76
211 #define GAME_PANEL_ELEMENT_1 77
212 #define GAME_PANEL_ELEMENT_2 78
213 #define GAME_PANEL_ELEMENT_3 79
214 #define GAME_PANEL_ELEMENT_4 80
215 #define GAME_PANEL_ELEMENT_5 81
216 #define GAME_PANEL_ELEMENT_6 82
217 #define GAME_PANEL_ELEMENT_7 83
218 #define GAME_PANEL_ELEMENT_8 84
219 #define GAME_PANEL_ELEMENT_COUNT_1 85
220 #define GAME_PANEL_ELEMENT_COUNT_2 86
221 #define GAME_PANEL_ELEMENT_COUNT_3 87
222 #define GAME_PANEL_ELEMENT_COUNT_4 88
223 #define GAME_PANEL_ELEMENT_COUNT_5 89
224 #define GAME_PANEL_ELEMENT_COUNT_6 90
225 #define GAME_PANEL_ELEMENT_COUNT_7 91
226 #define GAME_PANEL_ELEMENT_COUNT_8 92
227 #define GAME_PANEL_CE_SCORE_1 93
228 #define GAME_PANEL_CE_SCORE_2 94
229 #define GAME_PANEL_CE_SCORE_3 95
230 #define GAME_PANEL_CE_SCORE_4 96
231 #define GAME_PANEL_CE_SCORE_5 97
232 #define GAME_PANEL_CE_SCORE_6 98
233 #define GAME_PANEL_CE_SCORE_7 99
234 #define GAME_PANEL_CE_SCORE_8 100
235 #define GAME_PANEL_CE_SCORE_1_ELEMENT 101
236 #define GAME_PANEL_CE_SCORE_2_ELEMENT 102
237 #define GAME_PANEL_CE_SCORE_3_ELEMENT 103
238 #define GAME_PANEL_CE_SCORE_4_ELEMENT 104
239 #define GAME_PANEL_CE_SCORE_5_ELEMENT 105
240 #define GAME_PANEL_CE_SCORE_6_ELEMENT 106
241 #define GAME_PANEL_CE_SCORE_7_ELEMENT 107
242 #define GAME_PANEL_CE_SCORE_8_ELEMENT 108
243 #define GAME_PANEL_PLAYER_NAME 109
244 #define GAME_PANEL_LEVEL_NAME 110
245 #define GAME_PANEL_LEVEL_AUTHOR 111
247 #define NUM_GAME_PANEL_CONTROLS 112
249 struct GamePanelOrderInfo
255 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
257 struct GamePanelControlInfo
261 struct TextPosInfo *pos;
264 int value, last_value;
265 int frame, last_frame;
270 static struct GamePanelControlInfo game_panel_controls[] =
273 GAME_PANEL_LEVEL_NUMBER,
274 &game.panel.level_number,
283 GAME_PANEL_INVENTORY_COUNT,
284 &game.panel.inventory_count,
288 GAME_PANEL_INVENTORY_FIRST_1,
289 &game.panel.inventory_first[0],
293 GAME_PANEL_INVENTORY_FIRST_2,
294 &game.panel.inventory_first[1],
298 GAME_PANEL_INVENTORY_FIRST_3,
299 &game.panel.inventory_first[2],
303 GAME_PANEL_INVENTORY_FIRST_4,
304 &game.panel.inventory_first[3],
308 GAME_PANEL_INVENTORY_FIRST_5,
309 &game.panel.inventory_first[4],
313 GAME_PANEL_INVENTORY_FIRST_6,
314 &game.panel.inventory_first[5],
318 GAME_PANEL_INVENTORY_FIRST_7,
319 &game.panel.inventory_first[6],
323 GAME_PANEL_INVENTORY_FIRST_8,
324 &game.panel.inventory_first[7],
328 GAME_PANEL_INVENTORY_LAST_1,
329 &game.panel.inventory_last[0],
333 GAME_PANEL_INVENTORY_LAST_2,
334 &game.panel.inventory_last[1],
338 GAME_PANEL_INVENTORY_LAST_3,
339 &game.panel.inventory_last[2],
343 GAME_PANEL_INVENTORY_LAST_4,
344 &game.panel.inventory_last[3],
348 GAME_PANEL_INVENTORY_LAST_5,
349 &game.panel.inventory_last[4],
353 GAME_PANEL_INVENTORY_LAST_6,
354 &game.panel.inventory_last[5],
358 GAME_PANEL_INVENTORY_LAST_7,
359 &game.panel.inventory_last[6],
363 GAME_PANEL_INVENTORY_LAST_8,
364 &game.panel.inventory_last[7],
408 GAME_PANEL_KEY_WHITE,
409 &game.panel.key_white,
413 GAME_PANEL_KEY_WHITE_COUNT,
414 &game.panel.key_white_count,
443 GAME_PANEL_SHIELD_NORMAL,
444 &game.panel.shield_normal,
448 GAME_PANEL_SHIELD_NORMAL_TIME,
449 &game.panel.shield_normal_time,
453 GAME_PANEL_SHIELD_DEADLY,
454 &game.panel.shield_deadly,
458 GAME_PANEL_SHIELD_DEADLY_TIME,
459 &game.panel.shield_deadly_time,
468 GAME_PANEL_EMC_MAGIC_BALL,
469 &game.panel.emc_magic_ball,
473 GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
474 &game.panel.emc_magic_ball_switch,
478 GAME_PANEL_LIGHT_SWITCH,
479 &game.panel.light_switch,
483 GAME_PANEL_LIGHT_SWITCH_TIME,
484 &game.panel.light_switch_time,
488 GAME_PANEL_TIMEGATE_SWITCH,
489 &game.panel.timegate_switch,
493 GAME_PANEL_TIMEGATE_SWITCH_TIME,
494 &game.panel.timegate_switch_time,
498 GAME_PANEL_SWITCHGATE_SWITCH,
499 &game.panel.switchgate_switch,
503 GAME_PANEL_EMC_LENSES,
504 &game.panel.emc_lenses,
508 GAME_PANEL_EMC_LENSES_TIME,
509 &game.panel.emc_lenses_time,
513 GAME_PANEL_EMC_MAGNIFIER,
514 &game.panel.emc_magnifier,
518 GAME_PANEL_EMC_MAGNIFIER_TIME,
519 &game.panel.emc_magnifier_time,
523 GAME_PANEL_BALLOON_SWITCH,
524 &game.panel.balloon_switch,
528 GAME_PANEL_DYNABOMB_NUMBER,
529 &game.panel.dynabomb_number,
533 GAME_PANEL_DYNABOMB_SIZE,
534 &game.panel.dynabomb_size,
538 GAME_PANEL_DYNABOMB_POWER,
539 &game.panel.dynabomb_power,
544 &game.panel.penguins,
548 GAME_PANEL_SOKOBAN_OBJECTS,
549 &game.panel.sokoban_objects,
553 GAME_PANEL_SOKOBAN_FIELDS,
554 &game.panel.sokoban_fields,
558 GAME_PANEL_ROBOT_WHEEL,
559 &game.panel.robot_wheel,
563 GAME_PANEL_CONVEYOR_BELT_1,
564 &game.panel.conveyor_belt[0],
568 GAME_PANEL_CONVEYOR_BELT_2,
569 &game.panel.conveyor_belt[1],
573 GAME_PANEL_CONVEYOR_BELT_3,
574 &game.panel.conveyor_belt[2],
578 GAME_PANEL_CONVEYOR_BELT_4,
579 &game.panel.conveyor_belt[3],
583 GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
584 &game.panel.conveyor_belt_switch[0],
588 GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
589 &game.panel.conveyor_belt_switch[1],
593 GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
594 &game.panel.conveyor_belt_switch[2],
598 GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
599 &game.panel.conveyor_belt_switch[3],
603 GAME_PANEL_MAGIC_WALL,
604 &game.panel.magic_wall,
608 GAME_PANEL_MAGIC_WALL_TIME,
609 &game.panel.magic_wall_time,
613 GAME_PANEL_GRAVITY_STATE,
614 &game.panel.gravity_state,
618 GAME_PANEL_GRAPHIC_1,
619 &game.panel.graphic[0],
623 GAME_PANEL_GRAPHIC_2,
624 &game.panel.graphic[1],
628 GAME_PANEL_GRAPHIC_3,
629 &game.panel.graphic[2],
633 GAME_PANEL_GRAPHIC_4,
634 &game.panel.graphic[3],
638 GAME_PANEL_GRAPHIC_5,
639 &game.panel.graphic[4],
643 GAME_PANEL_GRAPHIC_6,
644 &game.panel.graphic[5],
648 GAME_PANEL_GRAPHIC_7,
649 &game.panel.graphic[6],
653 GAME_PANEL_GRAPHIC_8,
654 &game.panel.graphic[7],
658 GAME_PANEL_ELEMENT_1,
659 &game.panel.element[0],
663 GAME_PANEL_ELEMENT_2,
664 &game.panel.element[1],
668 GAME_PANEL_ELEMENT_3,
669 &game.panel.element[2],
673 GAME_PANEL_ELEMENT_4,
674 &game.panel.element[3],
678 GAME_PANEL_ELEMENT_5,
679 &game.panel.element[4],
683 GAME_PANEL_ELEMENT_6,
684 &game.panel.element[5],
688 GAME_PANEL_ELEMENT_7,
689 &game.panel.element[6],
693 GAME_PANEL_ELEMENT_8,
694 &game.panel.element[7],
698 GAME_PANEL_ELEMENT_COUNT_1,
699 &game.panel.element_count[0],
703 GAME_PANEL_ELEMENT_COUNT_2,
704 &game.panel.element_count[1],
708 GAME_PANEL_ELEMENT_COUNT_3,
709 &game.panel.element_count[2],
713 GAME_PANEL_ELEMENT_COUNT_4,
714 &game.panel.element_count[3],
718 GAME_PANEL_ELEMENT_COUNT_5,
719 &game.panel.element_count[4],
723 GAME_PANEL_ELEMENT_COUNT_6,
724 &game.panel.element_count[5],
728 GAME_PANEL_ELEMENT_COUNT_7,
729 &game.panel.element_count[6],
733 GAME_PANEL_ELEMENT_COUNT_8,
734 &game.panel.element_count[7],
738 GAME_PANEL_CE_SCORE_1,
739 &game.panel.ce_score[0],
743 GAME_PANEL_CE_SCORE_2,
744 &game.panel.ce_score[1],
748 GAME_PANEL_CE_SCORE_3,
749 &game.panel.ce_score[2],
753 GAME_PANEL_CE_SCORE_4,
754 &game.panel.ce_score[3],
758 GAME_PANEL_CE_SCORE_5,
759 &game.panel.ce_score[4],
763 GAME_PANEL_CE_SCORE_6,
764 &game.panel.ce_score[5],
768 GAME_PANEL_CE_SCORE_7,
769 &game.panel.ce_score[6],
773 GAME_PANEL_CE_SCORE_8,
774 &game.panel.ce_score[7],
778 GAME_PANEL_CE_SCORE_1_ELEMENT,
779 &game.panel.ce_score_element[0],
783 GAME_PANEL_CE_SCORE_2_ELEMENT,
784 &game.panel.ce_score_element[1],
788 GAME_PANEL_CE_SCORE_3_ELEMENT,
789 &game.panel.ce_score_element[2],
793 GAME_PANEL_CE_SCORE_4_ELEMENT,
794 &game.panel.ce_score_element[3],
798 GAME_PANEL_CE_SCORE_5_ELEMENT,
799 &game.panel.ce_score_element[4],
803 GAME_PANEL_CE_SCORE_6_ELEMENT,
804 &game.panel.ce_score_element[5],
808 GAME_PANEL_CE_SCORE_7_ELEMENT,
809 &game.panel.ce_score_element[6],
813 GAME_PANEL_CE_SCORE_8_ELEMENT,
814 &game.panel.ce_score_element[7],
818 GAME_PANEL_PLAYER_NAME,
819 &game.panel.player_name,
823 GAME_PANEL_LEVEL_NAME,
824 &game.panel.level_name,
828 GAME_PANEL_LEVEL_AUTHOR,
829 &game.panel.level_author,
842 /* values for delayed check of falling and moving elements and for collision */
843 #define CHECK_DELAY_MOVING 3
844 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
845 #define CHECK_DELAY_COLLISION 2
846 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
848 /* values for initial player move delay (initial delay counter value) */
849 #define INITIAL_MOVE_DELAY_OFF -1
850 #define INITIAL_MOVE_DELAY_ON 0
852 /* values for player movement speed (which is in fact a delay value) */
853 #define MOVE_DELAY_MIN_SPEED 32
854 #define MOVE_DELAY_NORMAL_SPEED 8
855 #define MOVE_DELAY_HIGH_SPEED 4
856 #define MOVE_DELAY_MAX_SPEED 1
858 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
859 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
861 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
862 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
864 /* values for other actions */
865 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN (1)
867 #define MOVE_STEPSIZE_MAX (TILEX)
869 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
872 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
874 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
875 RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
877 RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
879 RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
881 (element_info[e].move_delay_random))
882 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
883 RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
886 RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
888 RND((c)->delay_random))
891 #define GET_VALID_RUNTIME_ELEMENT(e) \
892 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
894 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
895 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
896 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
897 (be) + (e) - EL_SELF)
899 #define GET_PLAYER_FROM_BITS(p) \
900 (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
902 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
903 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
904 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
905 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
906 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
907 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
908 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
909 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
910 RESOLVED_REFERENCE_ELEMENT(be, e) : \
913 #define CAN_GROW_INTO(e) \
914 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
916 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
917 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
921 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
922 (CAN_MOVE_INTO_ACID(e) && \
923 Feld[x][y] == EL_ACID) || \
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
927 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
928 (CAN_MOVE_INTO_ACID(e) && \
929 Feld[x][y] == EL_ACID) || \
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
933 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
935 (CAN_MOVE_INTO_ACID(e) && \
936 Feld[x][y] == EL_ACID) || \
937 (DONT_COLLIDE_WITH(e) && \
939 !PLAYER_ENEMY_PROTECTED(x, y))))
941 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
942 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
944 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
945 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
947 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
948 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
950 #define ANDROID_CAN_CLONE_FIELD(x, y) \
951 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
952 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
954 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
955 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
957 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
958 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
960 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
961 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
964 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
966 #define PIG_CAN_ENTER_FIELD(e, x, y) \
967 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
970 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
971 Feld[x][y] == EL_EM_EXIT_OPEN || \
972 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
973 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974 IS_FOOD_PENGUIN(Feld[x][y])))
975 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
976 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
978 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
979 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
981 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
982 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
984 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
985 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
986 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
988 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
990 #define CE_ENTER_FIELD_COND(e, x, y) \
991 (!IS_PLAYER(x, y) && \
992 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
994 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
995 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
997 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
998 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1000 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
1001 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
1002 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
1003 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1005 /* game button identifiers */
1006 #define GAME_CTRL_ID_STOP 0
1007 #define GAME_CTRL_ID_PAUSE 1
1008 #define GAME_CTRL_ID_PLAY 2
1009 #define SOUND_CTRL_ID_MUSIC 3
1010 #define SOUND_CTRL_ID_LOOPS 4
1011 #define SOUND_CTRL_ID_SIMPLE 5
1013 #define NUM_GAME_BUTTONS 6
1016 /* forward declaration for internal use */
1018 static void CreateField(int, int, int);
1020 static void ResetGfxAnimation(int, int);
1022 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1023 static void AdvanceFrameAndPlayerCounters(int);
1025 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1026 static boolean MovePlayer(struct PlayerInfo *, int, int);
1027 static void ScrollPlayer(struct PlayerInfo *, int);
1028 static void ScrollScreen(struct PlayerInfo *, int);
1030 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1032 static void InitBeltMovement(void);
1033 static void CloseAllOpenTimegates(void);
1034 static void CheckGravityMovement(struct PlayerInfo *);
1035 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1036 static void KillPlayerUnlessEnemyProtected(int, int);
1037 static void KillPlayerUnlessExplosionProtected(int, int);
1039 static void TestIfPlayerTouchesCustomElement(int, int);
1040 static void TestIfElementTouchesCustomElement(int, int);
1041 static void TestIfElementHitsCustomElement(int, int, int);
1043 static void TestIfElementSmashesCustomElement(int, int, int);
1046 static void HandleElementChange(int, int, int);
1047 static void ExecuteCustomElementAction(int, int, int, int);
1048 static boolean ChangeElement(int, int, int, int);
1050 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1051 #define CheckTriggeredElementChange(x, y, e, ev) \
1052 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1053 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
1054 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1055 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
1056 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1057 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
1058 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1060 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1061 #define CheckElementChange(x, y, e, te, ev) \
1062 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1063 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
1064 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1065 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
1066 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1068 static void PlayLevelSound(int, int, int);
1069 static void PlayLevelSoundNearest(int, int, int);
1070 static void PlayLevelSoundAction(int, int, int);
1071 static void PlayLevelSoundElementAction(int, int, int, int);
1072 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1073 static void PlayLevelSoundActionIfLoop(int, int, int);
1074 static void StopLevelSoundActionIfLoop(int, int, int);
1075 static void PlayLevelMusic();
1077 static void MapGameButtons();
1078 static void HandleGameButtons(struct GadgetInfo *);
1080 int AmoebeNachbarNr(int, int);
1081 void AmoebeUmwandeln(int, int);
1082 void ContinueMoving(int, int);
1083 void Bang(int, int);
1084 void InitMovDir(int, int);
1085 void InitAmoebaNr(int, int);
1086 int NewHiScore(void);
1088 void TestIfGoodThingHitsBadThing(int, int, int);
1089 void TestIfBadThingHitsGoodThing(int, int, int);
1090 void TestIfPlayerTouchesBadThing(int, int);
1091 void TestIfPlayerRunsIntoBadThing(int, int, int);
1092 void TestIfBadThingTouchesPlayer(int, int);
1093 void TestIfBadThingRunsIntoPlayer(int, int, int);
1094 void TestIfFriendTouchesBadThing(int, int);
1095 void TestIfBadThingTouchesFriend(int, int);
1096 void TestIfBadThingTouchesOtherBadThing(int, int);
1098 void KillPlayer(struct PlayerInfo *);
1099 void BuryPlayer(struct PlayerInfo *);
1100 void RemovePlayer(struct PlayerInfo *);
1102 boolean SnapField(struct PlayerInfo *, int, int);
1103 boolean DropElement(struct PlayerInfo *);
1105 static int getInvisibleActiveFromInvisibleElement(int);
1106 static int getInvisibleFromInvisibleActiveElement(int);
1108 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1110 /* for detection of endless loops, caused by custom element programming */
1111 /* (using maximal playfield width x 10 is just a rough approximation) */
1112 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
1114 #define RECURSION_LOOP_DETECTION_START(e, rc) \
1116 if (recursion_loop_detected) \
1119 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
1121 recursion_loop_detected = TRUE; \
1122 recursion_loop_element = (e); \
1125 recursion_loop_depth++; \
1128 #define RECURSION_LOOP_DETECTION_END() \
1130 recursion_loop_depth--; \
1133 static int recursion_loop_depth;
1134 static boolean recursion_loop_detected;
1135 static boolean recursion_loop_element;
1138 /* ------------------------------------------------------------------------- */
1139 /* definition of elements that automatically change to other elements after */
1140 /* a specified time, eventually calling a function when changing */
1141 /* ------------------------------------------------------------------------- */
1143 /* forward declaration for changer functions */
1144 static void InitBuggyBase(int, int);
1145 static void WarnBuggyBase(int, int);
1147 static void InitTrap(int, int);
1148 static void ActivateTrap(int, int);
1149 static void ChangeActiveTrap(int, int);
1151 static void InitRobotWheel(int, int);
1152 static void RunRobotWheel(int, int);
1153 static void StopRobotWheel(int, int);
1155 static void InitTimegateWheel(int, int);
1156 static void RunTimegateWheel(int, int);
1158 static void InitMagicBallDelay(int, int);
1159 static void ActivateMagicBall(int, int);
1161 struct ChangingElementInfo
1166 void (*pre_change_function)(int x, int y);
1167 void (*change_function)(int x, int y);
1168 void (*post_change_function)(int x, int y);
1171 static struct ChangingElementInfo change_delay_list[] =
1206 EL_STEEL_EXIT_OPENING,
1214 EL_STEEL_EXIT_CLOSING,
1215 EL_STEEL_EXIT_CLOSED,
1242 EL_EM_STEEL_EXIT_OPENING,
1243 EL_EM_STEEL_EXIT_OPEN,
1250 EL_EM_STEEL_EXIT_CLOSING,
1254 EL_EM_STEEL_EXIT_CLOSED,
1278 EL_SWITCHGATE_OPENING,
1286 EL_SWITCHGATE_CLOSING,
1287 EL_SWITCHGATE_CLOSED,
1294 EL_TIMEGATE_OPENING,
1302 EL_TIMEGATE_CLOSING,
1311 EL_ACID_SPLASH_LEFT,
1319 EL_ACID_SPLASH_RIGHT,
1328 EL_SP_BUGGY_BASE_ACTIVATING,
1335 EL_SP_BUGGY_BASE_ACTIVATING,
1336 EL_SP_BUGGY_BASE_ACTIVE,
1343 EL_SP_BUGGY_BASE_ACTIVE,
1367 EL_ROBOT_WHEEL_ACTIVE,
1375 EL_TIMEGATE_SWITCH_ACTIVE,
1383 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1384 EL_DC_TIMEGATE_SWITCH,
1391 EL_EMC_MAGIC_BALL_ACTIVE,
1392 EL_EMC_MAGIC_BALL_ACTIVE,
1399 EL_EMC_SPRING_BUMPER_ACTIVE,
1400 EL_EMC_SPRING_BUMPER,
1407 EL_DIAGONAL_SHRINKING,
1415 EL_DIAGONAL_GROWING,
1436 int push_delay_fixed, push_delay_random;
1440 { EL_SPRING, 0, 0 },
1441 { EL_BALLOON, 0, 0 },
1443 { EL_SOKOBAN_OBJECT, 2, 0 },
1444 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1445 { EL_SATELLITE, 2, 0 },
1446 { EL_SP_DISK_YELLOW, 2, 0 },
1448 { EL_UNDEFINED, 0, 0 },
1456 move_stepsize_list[] =
1458 { EL_AMOEBA_DROP, 2 },
1459 { EL_AMOEBA_DROPPING, 2 },
1460 { EL_QUICKSAND_FILLING, 1 },
1461 { EL_QUICKSAND_EMPTYING, 1 },
1462 { EL_QUICKSAND_FAST_FILLING, 2 },
1463 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1464 { EL_MAGIC_WALL_FILLING, 2 },
1465 { EL_MAGIC_WALL_EMPTYING, 2 },
1466 { EL_BD_MAGIC_WALL_FILLING, 2 },
1467 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1468 { EL_DC_MAGIC_WALL_FILLING, 2 },
1469 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1471 { EL_UNDEFINED, 0 },
1479 collect_count_list[] =
1482 { EL_BD_DIAMOND, 1 },
1483 { EL_EMERALD_YELLOW, 1 },
1484 { EL_EMERALD_RED, 1 },
1485 { EL_EMERALD_PURPLE, 1 },
1487 { EL_SP_INFOTRON, 1 },
1491 { EL_UNDEFINED, 0 },
1499 access_direction_list[] =
1501 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1502 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1503 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1504 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1505 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1506 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1507 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1508 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1509 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1510 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1511 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1513 { EL_SP_PORT_LEFT, MV_RIGHT },
1514 { EL_SP_PORT_RIGHT, MV_LEFT },
1515 { EL_SP_PORT_UP, MV_DOWN },
1516 { EL_SP_PORT_DOWN, MV_UP },
1517 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1518 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1519 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1520 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1521 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1522 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1523 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1524 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1525 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1526 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1527 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1528 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1529 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1530 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1531 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1533 { EL_UNDEFINED, MV_NONE }
1536 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1538 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1539 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1540 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1541 IS_JUST_CHANGING(x, y))
1543 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1545 /* static variables for playfield scan mode (scanning forward or backward) */
1546 static int playfield_scan_start_x = 0;
1547 static int playfield_scan_start_y = 0;
1548 static int playfield_scan_delta_x = 1;
1549 static int playfield_scan_delta_y = 1;
1551 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1552 (y) >= 0 && (y) <= lev_fieldy - 1; \
1553 (y) += playfield_scan_delta_y) \
1554 for ((x) = playfield_scan_start_x; \
1555 (x) >= 0 && (x) <= lev_fieldx - 1; \
1556 (x) += playfield_scan_delta_x)
1559 void DEBUG_SetMaximumDynamite()
1563 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1564 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1565 local_player->inventory_element[local_player->inventory_size++] =
1570 static void InitPlayfieldScanModeVars()
1572 if (game.use_reverse_scan_direction)
1574 playfield_scan_start_x = lev_fieldx - 1;
1575 playfield_scan_start_y = lev_fieldy - 1;
1577 playfield_scan_delta_x = -1;
1578 playfield_scan_delta_y = -1;
1582 playfield_scan_start_x = 0;
1583 playfield_scan_start_y = 0;
1585 playfield_scan_delta_x = 1;
1586 playfield_scan_delta_y = 1;
1590 static void InitPlayfieldScanMode(int mode)
1592 game.use_reverse_scan_direction =
1593 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1595 InitPlayfieldScanModeVars();
1598 static int get_move_delay_from_stepsize(int move_stepsize)
1601 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1603 /* make sure that stepsize value is always a power of 2 */
1604 move_stepsize = (1 << log_2(move_stepsize));
1606 return TILEX / move_stepsize;
1609 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1612 int player_nr = player->index_nr;
1613 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1614 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1616 /* do no immediately change move delay -- the player might just be moving */
1617 player->move_delay_value_next = move_delay;
1619 /* information if player can move must be set separately */
1620 player->cannot_move = cannot_move;
1624 player->move_delay = game.initial_move_delay[player_nr];
1625 player->move_delay_value = game.initial_move_delay_value[player_nr];
1627 player->move_delay_value_next = -1;
1629 player->move_delay_reset_counter = 0;
1633 void GetPlayerConfig()
1635 GameFrameDelay = setup.game_frame_delay;
1637 if (!audio.sound_available)
1638 setup.sound_simple = FALSE;
1640 if (!audio.loops_available)
1641 setup.sound_loops = FALSE;
1643 if (!audio.music_available)
1644 setup.sound_music = FALSE;
1646 if (!video.fullscreen_available)
1647 setup.fullscreen = FALSE;
1649 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1651 SetAudioMode(setup.sound);
1655 int GetElementFromGroupElement(int element)
1657 if (IS_GROUP_ELEMENT(element))
1659 struct ElementGroupInfo *group = element_info[element].group;
1660 int last_anim_random_frame = gfx.anim_random_frame;
1663 if (group->choice_mode == ANIM_RANDOM)
1664 gfx.anim_random_frame = RND(group->num_elements_resolved);
1666 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1667 group->choice_mode, 0,
1670 if (group->choice_mode == ANIM_RANDOM)
1671 gfx.anim_random_frame = last_anim_random_frame;
1673 group->choice_pos++;
1675 element = group->element_resolved[element_pos];
1681 static void InitPlayerField(int x, int y, int element, boolean init_game)
1683 if (element == EL_SP_MURPHY)
1687 if (stored_player[0].present)
1689 Feld[x][y] = EL_SP_MURPHY_CLONE;
1695 stored_player[0].use_murphy = TRUE;
1697 if (!level.use_artwork_element[0])
1698 stored_player[0].artwork_element = EL_SP_MURPHY;
1701 Feld[x][y] = EL_PLAYER_1;
1707 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1708 int jx = player->jx, jy = player->jy;
1710 player->present = TRUE;
1712 player->block_last_field = (element == EL_SP_MURPHY ?
1713 level.sp_block_last_field :
1714 level.block_last_field);
1716 /* ---------- initialize player's last field block delay --------------- */
1718 /* always start with reliable default value (no adjustment needed) */
1719 player->block_delay_adjustment = 0;
1721 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1722 if (player->block_last_field && element == EL_SP_MURPHY)
1723 player->block_delay_adjustment = 1;
1725 /* special case 2: in game engines before 3.1.1, blocking was different */
1726 if (game.use_block_last_field_bug)
1727 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1729 if (!options.network || player->connected)
1731 player->active = TRUE;
1733 /* remove potentially duplicate players */
1734 if (StorePlayer[jx][jy] == Feld[x][y])
1735 StorePlayer[jx][jy] = 0;
1737 StorePlayer[x][y] = Feld[x][y];
1741 printf("Player %d activated.\n", player->element_nr);
1742 printf("[Local player is %d and currently %s.]\n",
1743 local_player->element_nr,
1744 local_player->active ? "active" : "not active");
1748 Feld[x][y] = EL_EMPTY;
1750 player->jx = player->last_jx = x;
1751 player->jy = player->last_jy = y;
1755 static void InitField(int x, int y, boolean init_game)
1757 int element = Feld[x][y];
1766 InitPlayerField(x, y, element, init_game);
1769 case EL_SOKOBAN_FIELD_PLAYER:
1770 element = Feld[x][y] = EL_PLAYER_1;
1771 InitField(x, y, init_game);
1773 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1774 InitField(x, y, init_game);
1777 case EL_SOKOBAN_FIELD_EMPTY:
1778 local_player->sokobanfields_still_needed++;
1782 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1783 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1784 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1785 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1786 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1787 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1788 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1789 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1790 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1791 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1800 case EL_SPACESHIP_RIGHT:
1801 case EL_SPACESHIP_UP:
1802 case EL_SPACESHIP_LEFT:
1803 case EL_SPACESHIP_DOWN:
1804 case EL_BD_BUTTERFLY:
1805 case EL_BD_BUTTERFLY_RIGHT:
1806 case EL_BD_BUTTERFLY_UP:
1807 case EL_BD_BUTTERFLY_LEFT:
1808 case EL_BD_BUTTERFLY_DOWN:
1810 case EL_BD_FIREFLY_RIGHT:
1811 case EL_BD_FIREFLY_UP:
1812 case EL_BD_FIREFLY_LEFT:
1813 case EL_BD_FIREFLY_DOWN:
1814 case EL_PACMAN_RIGHT:
1816 case EL_PACMAN_LEFT:
1817 case EL_PACMAN_DOWN:
1819 case EL_YAMYAM_LEFT:
1820 case EL_YAMYAM_RIGHT:
1822 case EL_YAMYAM_DOWN:
1823 case EL_DARK_YAMYAM:
1826 case EL_SP_SNIKSNAK:
1827 case EL_SP_ELECTRON:
1836 case EL_AMOEBA_FULL:
1841 case EL_AMOEBA_DROP:
1842 if (y == lev_fieldy - 1)
1844 Feld[x][y] = EL_AMOEBA_GROWING;
1845 Store[x][y] = EL_AMOEBA_WET;
1849 case EL_DYNAMITE_ACTIVE:
1850 case EL_SP_DISK_RED_ACTIVE:
1851 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1852 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1853 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1854 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1855 MovDelay[x][y] = 96;
1858 case EL_EM_DYNAMITE_ACTIVE:
1859 MovDelay[x][y] = 32;
1863 local_player->lights_still_needed++;
1867 local_player->friends_still_needed++;
1872 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1875 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1876 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1877 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1878 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1879 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1880 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1881 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1882 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1883 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1884 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1885 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1886 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1889 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1890 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1891 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1893 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1895 game.belt_dir[belt_nr] = belt_dir;
1896 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1898 else /* more than one switch -- set it like the first switch */
1900 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1905 #if !USE_BOTH_SWITCHGATE_SWITCHES
1906 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1908 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1911 case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1913 Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1917 case EL_LIGHT_SWITCH_ACTIVE:
1919 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1922 case EL_INVISIBLE_STEELWALL:
1923 case EL_INVISIBLE_WALL:
1924 case EL_INVISIBLE_SAND:
1925 if (game.light_time_left > 0 ||
1926 game.lenses_time_left > 0)
1927 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1930 case EL_EMC_MAGIC_BALL:
1931 if (game.ball_state)
1932 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1935 case EL_EMC_MAGIC_BALL_SWITCH:
1936 if (game.ball_state)
1937 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1941 if (IS_CUSTOM_ELEMENT(element))
1943 if (CAN_MOVE(element))
1946 #if USE_NEW_CUSTOM_VALUE
1947 if (!element_info[element].use_last_ce_value || init_game)
1948 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1951 else if (IS_GROUP_ELEMENT(element))
1953 Feld[x][y] = GetElementFromGroupElement(element);
1955 InitField(x, y, init_game);
1962 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1965 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1967 InitField(x, y, init_game);
1969 /* not needed to call InitMovDir() -- already done by InitField()! */
1970 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1971 CAN_MOVE(Feld[x][y]))
1975 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1977 int old_element = Feld[x][y];
1979 InitField(x, y, init_game);
1981 /* not needed to call InitMovDir() -- already done by InitField()! */
1982 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1983 CAN_MOVE(old_element) &&
1984 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1987 /* this case is in fact a combination of not less than three bugs:
1988 first, it calls InitMovDir() for elements that can move, although this is
1989 already done by InitField(); then, it checks the element that was at this
1990 field _before_ the call to InitField() (which can change it); lastly, it
1991 was not called for "mole with direction" elements, which were treated as
1992 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1998 static int get_key_element_from_nr(int key_nr)
2000 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2001 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2002 EL_EM_KEY_1 : EL_KEY_1);
2004 return key_base_element + key_nr;
2007 static int get_next_dropped_element(struct PlayerInfo *player)
2009 return (player->inventory_size > 0 ?
2010 player->inventory_element[player->inventory_size - 1] :
2011 player->inventory_infinite_element != EL_UNDEFINED ?
2012 player->inventory_infinite_element :
2013 player->dynabombs_left > 0 ?
2014 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2018 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2020 /* pos >= 0: get element from bottom of the stack;
2021 pos < 0: get element from top of the stack */
2025 int min_inventory_size = -pos;
2026 int inventory_pos = player->inventory_size - min_inventory_size;
2027 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2029 return (player->inventory_size >= min_inventory_size ?
2030 player->inventory_element[inventory_pos] :
2031 player->inventory_infinite_element != EL_UNDEFINED ?
2032 player->inventory_infinite_element :
2033 player->dynabombs_left >= min_dynabombs_left ?
2034 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2039 int min_dynabombs_left = pos + 1;
2040 int min_inventory_size = pos + 1 - player->dynabombs_left;
2041 int inventory_pos = pos - player->dynabombs_left;
2043 return (player->inventory_infinite_element != EL_UNDEFINED ?
2044 player->inventory_infinite_element :
2045 player->dynabombs_left >= min_dynabombs_left ?
2046 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2047 player->inventory_size >= min_inventory_size ?
2048 player->inventory_element[inventory_pos] :
2053 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2055 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2056 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2059 if (gpo1->sort_priority != gpo2->sort_priority)
2060 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2062 compare_result = gpo1->nr - gpo2->nr;
2064 return compare_result;
2067 void InitGameControlValues()
2071 for (i = 0; game_panel_controls[i].nr != -1; i++)
2073 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2074 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2075 struct TextPosInfo *pos = gpc->pos;
2077 int type = gpc->type;
2081 Error(ERR_INFO, "'game_panel_controls' structure corrupted");
2082 Error(ERR_EXIT, "this should not happen -- please debug");
2085 /* force update of game controls after initialization */
2086 gpc->value = gpc->last_value = -1;
2087 gpc->frame = gpc->last_frame = -1;
2088 gpc->gfx_frame = -1;
2090 /* determine panel value width for later calculation of alignment */
2091 if (type == TYPE_INTEGER || type == TYPE_STRING)
2093 pos->width = pos->size * getFontWidth(pos->font);
2094 pos->height = getFontHeight(pos->font);
2096 else if (type == TYPE_ELEMENT)
2098 pos->width = pos->size;
2099 pos->height = pos->size;
2102 /* fill structure for game panel draw order */
2104 gpo->sort_priority = pos->sort_priority;
2107 /* sort game panel controls according to sort_priority and control number */
2108 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2109 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2112 void UpdatePlayfieldElementCount()
2114 boolean use_element_count = FALSE;
2117 /* first check if it is needed at all to calculate playfield element count */
2118 for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2119 if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2120 use_element_count = TRUE;
2122 if (!use_element_count)
2125 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2126 element_info[i].element_count = 0;
2128 SCAN_PLAYFIELD(x, y)
2130 element_info[Feld[x][y]].element_count++;
2133 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2134 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2135 if (IS_IN_GROUP(j, i))
2136 element_info[EL_GROUP_START + i].element_count +=
2137 element_info[j].element_count;
2140 void UpdateGameControlValues()
2143 int time = (local_player->LevelSolved ?
2144 local_player->LevelSolved_CountingTime :
2145 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2146 level.native_em_level->lev->time :
2147 level.time == 0 ? TimePlayed : TimeLeft);
2148 int score = (local_player->LevelSolved ?
2149 local_player->LevelSolved_CountingScore :
2150 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2151 level.native_em_level->lev->score :
2152 local_player->score);
2153 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2154 level.native_em_level->lev->required :
2155 local_player->gems_still_needed);
2156 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2157 level.native_em_level->lev->required > 0 :
2158 local_player->gems_still_needed > 0 ||
2159 local_player->sokobanfields_still_needed > 0 ||
2160 local_player->lights_still_needed > 0);
2162 UpdatePlayfieldElementCount();
2164 /* update game panel control values */
2166 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2167 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2169 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2170 for (i = 0; i < MAX_NUM_KEYS; i++)
2171 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2172 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2173 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2175 if (game.centered_player_nr == -1)
2177 for (i = 0; i < MAX_PLAYERS; i++)
2179 for (k = 0; k < MAX_NUM_KEYS; k++)
2181 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2183 if (level.native_em_level->ply[i]->keys & (1 << k))
2184 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2185 get_key_element_from_nr(k);
2187 else if (stored_player[i].key[k])
2188 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2189 get_key_element_from_nr(k);
2192 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2193 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2194 level.native_em_level->ply[i]->dynamite;
2196 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2197 stored_player[i].inventory_size;
2199 if (stored_player[i].num_white_keys > 0)
2200 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2203 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2204 stored_player[i].num_white_keys;
2209 int player_nr = game.centered_player_nr;
2211 for (k = 0; k < MAX_NUM_KEYS; k++)
2213 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2215 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2216 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2217 get_key_element_from_nr(k);
2219 else if (stored_player[player_nr].key[k])
2220 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2221 get_key_element_from_nr(k);
2224 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2225 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2226 level.native_em_level->ply[player_nr]->dynamite;
2228 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2229 stored_player[player_nr].inventory_size;
2231 if (stored_player[player_nr].num_white_keys > 0)
2232 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2234 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2235 stored_player[player_nr].num_white_keys;
2238 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2240 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2241 get_inventory_element_from_pos(local_player, i);
2242 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2243 get_inventory_element_from_pos(local_player, -i - 1);
2246 game_panel_controls[GAME_PANEL_SCORE].value = score;
2248 game_panel_controls[GAME_PANEL_TIME].value = time;
2250 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2251 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2252 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2254 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2255 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2257 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2258 local_player->shield_normal_time_left;
2259 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2260 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2262 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2263 local_player->shield_deadly_time_left;
2265 game_panel_controls[GAME_PANEL_EXIT].value =
2266 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2268 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2269 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2270 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2271 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2272 EL_EMC_MAGIC_BALL_SWITCH);
2274 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2275 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2276 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2277 game.light_time_left;
2279 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2280 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2281 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2282 game.timegate_time_left;
2284 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2285 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2287 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2288 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2289 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2290 game.lenses_time_left;
2292 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2293 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2294 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2295 game.magnify_time_left;
2297 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2298 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2299 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2300 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2301 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2302 EL_BALLOON_SWITCH_NONE);
2304 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2305 local_player->dynabomb_count;
2306 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2307 local_player->dynabomb_size;
2308 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2309 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2311 game_panel_controls[GAME_PANEL_PENGUINS].value =
2312 local_player->friends_still_needed;
2314 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2315 local_player->sokobanfields_still_needed;
2316 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2317 local_player->sokobanfields_still_needed;
2319 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2320 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2322 for (i = 0; i < NUM_BELTS; i++)
2324 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2325 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2326 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2327 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2328 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2331 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2332 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2333 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2334 game.magic_wall_time_left;
2336 #if USE_PLAYER_GRAVITY
2337 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2338 local_player->gravity;
2340 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2343 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2344 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2346 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2347 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2348 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2349 game.panel.element[i].id : EL_UNDEFINED);
2351 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2352 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2353 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2354 element_info[game.panel.element_count[i].id].element_count : 0);
2356 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2357 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2358 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2359 element_info[game.panel.ce_score[i].id].collect_score : 0);
2361 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2362 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2363 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2364 element_info[game.panel.ce_score_element[i].id].collect_score :
2367 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2368 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2369 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2371 /* update game panel control frames */
2373 for (i = 0; game_panel_controls[i].nr != -1; i++)
2375 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2377 if (gpc->type == TYPE_ELEMENT)
2379 int last_anim_random_frame = gfx.anim_random_frame;
2380 int element = gpc->value;
2381 int graphic = el2panelimg(element);
2383 if (gpc->value != gpc->last_value)
2386 gpc->gfx_random = INIT_GFX_RANDOM();
2392 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2393 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2394 gpc->gfx_random = INIT_GFX_RANDOM();
2397 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2398 gfx.anim_random_frame = gpc->gfx_random;
2400 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2401 gpc->gfx_frame = element_info[element].collect_score;
2403 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2406 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2407 gfx.anim_random_frame = last_anim_random_frame;
2412 void DisplayGameControlValues()
2414 boolean redraw_panel = FALSE;
2417 for (i = 0; game_panel_controls[i].nr != -1; i++)
2419 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2421 if (PANEL_DEACTIVATED(gpc->pos))
2424 if (gpc->value == gpc->last_value &&
2425 gpc->frame == gpc->last_frame)
2428 redraw_panel = TRUE;
2434 /* copy default game door content to main double buffer */
2435 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2436 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2438 /* redraw game control buttons */
2440 RedrawGameButtons();
2446 game_status = GAME_MODE_PSEUDO_PANEL;
2449 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2451 for (i = 0; game_panel_controls[i].nr != -1; i++)
2455 int nr = game_panel_order[i].nr;
2456 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2458 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2461 struct TextPosInfo *pos = gpc->pos;
2462 int type = gpc->type;
2463 int value = gpc->value;
2464 int frame = gpc->frame;
2466 int last_value = gpc->last_value;
2467 int last_frame = gpc->last_frame;
2469 int size = pos->size;
2470 int font = pos->font;
2471 boolean draw_masked = pos->draw_masked;
2472 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2474 if (PANEL_DEACTIVATED(pos))
2478 if (value == last_value && frame == last_frame)
2482 gpc->last_value = value;
2483 gpc->last_frame = frame;
2486 printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2489 if (type == TYPE_INTEGER)
2491 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2492 nr == GAME_PANEL_TIME)
2494 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2496 if (use_dynamic_size) /* use dynamic number of digits */
2498 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2499 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2500 int size2 = size1 + 1;
2501 int font1 = pos->font;
2502 int font2 = pos->font_alt;
2504 size = (value < value_change ? size1 : size2);
2505 font = (value < value_change ? font1 : font2);
2508 /* clear background if value just changed its size (dynamic digits) */
2509 if ((last_value < value_change) != (value < value_change))
2511 int width1 = size1 * getFontWidth(font1);
2512 int width2 = size2 * getFontWidth(font2);
2513 int max_width = MAX(width1, width2);
2514 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2516 pos->width = max_width;
2518 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2519 max_width, max_height);
2526 /* correct text size if "digits" is zero or less */
2528 size = strlen(int2str(value, size));
2530 /* dynamically correct text alignment */
2531 pos->width = size * getFontWidth(font);
2534 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2535 int2str(value, size), font, mask_mode);
2537 else if (type == TYPE_ELEMENT)
2539 int element, graphic;
2543 int dst_x = PANEL_XPOS(pos);
2544 int dst_y = PANEL_YPOS(pos);
2547 if (value != EL_UNDEFINED && value != EL_EMPTY)
2550 graphic = el2panelimg(value);
2552 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2555 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2559 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2562 width = graphic_info[graphic].width * size / TILESIZE;
2563 height = graphic_info[graphic].height * size / TILESIZE;
2567 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2568 dst_x - src_x, dst_y - src_y);
2569 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2574 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2579 if (value == EL_UNDEFINED || value == EL_EMPTY)
2581 element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2582 graphic = el2panelimg(element);
2584 src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2585 src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2586 src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2591 graphic = el2panelimg(value);
2593 getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2596 width = graphic_info[graphic].width * size / TILESIZE;
2597 height = graphic_info[graphic].height * size / TILESIZE;
2599 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2602 else if (type == TYPE_STRING)
2604 boolean active = (value != 0);
2605 char *state_normal = "off";
2606 char *state_active = "on";
2607 char *state = (active ? state_active : state_normal);
2608 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2609 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2610 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2611 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2613 if (nr == GAME_PANEL_GRAVITY_STATE)
2615 int font1 = pos->font; /* (used for normal state) */
2616 int font2 = pos->font_alt; /* (used for active state) */
2618 int size1 = strlen(state_normal);
2619 int size2 = strlen(state_active);
2620 int width1 = size1 * getFontWidth(font1);
2621 int width2 = size2 * getFontWidth(font2);
2622 int max_width = MAX(width1, width2);
2623 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2625 pos->width = max_width;
2627 /* clear background for values that may have changed its size */
2628 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2629 max_width, max_height);
2632 font = (active ? font2 : font1);
2642 /* don't truncate output if "chars" is zero or less */
2645 /* dynamically correct text alignment */
2646 pos->width = size * getFontWidth(font);
2650 s_cut = getStringCopyN(s, size);
2652 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2653 s_cut, font, mask_mode);
2659 redraw_mask |= REDRAW_DOOR_1;
2662 game_status = GAME_MODE_PLAYING;
2665 void UpdateAndDisplayGameControlValues()
2667 if (tape.warp_forward)
2670 UpdateGameControlValues();
2671 DisplayGameControlValues();
2674 void DrawGameValue_Emeralds(int value)
2676 struct TextPosInfo *pos = &game.panel.gems;
2678 int font_nr = pos->font;
2680 int font_nr = FONT_TEXT_2;
2682 int font_width = getFontWidth(font_nr);
2683 int chars = pos->size;
2686 return; /* !!! USE NEW STUFF !!! */
2689 if (PANEL_DEACTIVATED(pos))
2692 pos->width = chars * font_width;
2694 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2697 void DrawGameValue_Dynamite(int value)
2699 struct TextPosInfo *pos = &game.panel.inventory_count;
2701 int font_nr = pos->font;
2703 int font_nr = FONT_TEXT_2;
2705 int font_width = getFontWidth(font_nr);
2706 int chars = pos->size;
2709 return; /* !!! USE NEW STUFF !!! */
2712 if (PANEL_DEACTIVATED(pos))
2715 pos->width = chars * font_width;
2717 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2720 void DrawGameValue_Score(int value)
2722 struct TextPosInfo *pos = &game.panel.score;
2724 int font_nr = pos->font;
2726 int font_nr = FONT_TEXT_2;
2728 int font_width = getFontWidth(font_nr);
2729 int chars = pos->size;
2732 return; /* !!! USE NEW STUFF !!! */
2735 if (PANEL_DEACTIVATED(pos))
2738 pos->width = chars * font_width;
2740 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2743 void DrawGameValue_Time(int value)
2745 struct TextPosInfo *pos = &game.panel.time;
2746 static int last_value = -1;
2749 int chars = pos->size;
2751 int font1_nr = pos->font;
2752 int font2_nr = pos->font_alt;
2754 int font1_nr = FONT_TEXT_2;
2755 int font2_nr = FONT_TEXT_1;
2757 int font_nr = font1_nr;
2758 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2761 return; /* !!! USE NEW STUFF !!! */
2764 if (PANEL_DEACTIVATED(pos))
2767 if (use_dynamic_chars) /* use dynamic number of chars */
2769 chars = (value < 1000 ? chars1 : chars2);
2770 font_nr = (value < 1000 ? font1_nr : font2_nr);
2773 /* clear background if value just changed its size (dynamic chars only) */
2774 if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2776 int width1 = chars1 * getFontWidth(font1_nr);
2777 int width2 = chars2 * getFontWidth(font2_nr);
2778 int max_width = MAX(width1, width2);
2779 int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2781 pos->width = max_width;
2783 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2784 max_width, max_height);
2787 pos->width = chars * getFontWidth(font_nr);
2789 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2794 void DrawGameValue_Level(int value)
2796 struct TextPosInfo *pos = &game.panel.level_number;
2799 int chars = pos->size;
2801 int font1_nr = pos->font;
2802 int font2_nr = pos->font_alt;
2804 int font1_nr = FONT_TEXT_2;
2805 int font2_nr = FONT_TEXT_1;
2807 int font_nr = font1_nr;
2808 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2811 return; /* !!! USE NEW STUFF !!! */
2814 if (PANEL_DEACTIVATED(pos))
2817 if (use_dynamic_chars) /* use dynamic number of chars */
2819 chars = (level_nr < 100 ? chars1 : chars2);
2820 font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2823 pos->width = chars * getFontWidth(font_nr);
2825 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2828 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2831 struct TextPosInfo *pos = &game.panel.keys;
2834 int base_key_graphic = EL_KEY_1;
2839 return; /* !!! USE NEW STUFF !!! */
2843 if (PANEL_DEACTIVATED(pos))
2848 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2849 base_key_graphic = EL_EM_KEY_1;
2853 pos->width = 4 * MINI_TILEX;
2857 for (i = 0; i < MAX_NUM_KEYS; i++)
2859 /* currently only 4 of 8 possible keys are displayed */
2860 for (i = 0; i < STD_NUM_KEYS; i++)
2864 struct TextPosInfo *pos = &game.panel.key[i];
2866 int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2867 int src_y = DOOR_GFX_PAGEY1 + 123;
2869 int dst_x = PANEL_XPOS(pos);
2870 int dst_y = PANEL_YPOS(pos);
2872 int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2873 int dst_y = PANEL_YPOS(pos);
2877 int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2878 level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2880 int graphic = el2edimg(element);
2884 if (PANEL_DEACTIVATED(pos))
2889 /* masked blit with tiles from half-size scaled bitmap does not work yet
2890 (no mask bitmap created for these sizes after loading and scaling) --
2891 solution: load without creating mask, scale, then create final mask */
2893 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2894 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2899 int graphic = el2edimg(base_key_graphic + i);
2904 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2906 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2907 dst_x - src_x, dst_y - src_y);
2908 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2914 DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2916 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2917 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2920 DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
2922 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2923 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2931 void DrawGameValue_Emeralds(int value)
2933 int font_nr = FONT_TEXT_2;
2934 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2936 if (PANEL_DEACTIVATED(game.panel.gems))
2939 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
2942 void DrawGameValue_Dynamite(int value)
2944 int font_nr = FONT_TEXT_2;
2945 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2947 if (PANEL_DEACTIVATED(game.panel.inventory_count))
2950 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
2953 void DrawGameValue_Score(int value)
2955 int font_nr = FONT_TEXT_2;
2956 int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
2958 if (PANEL_DEACTIVATED(game.panel.score))
2961 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
2964 void DrawGameValue_Time(int value)
2966 int font1_nr = FONT_TEXT_2;
2968 int font2_nr = FONT_TEXT_1;
2970 int font2_nr = FONT_LEVEL_NUMBER;
2972 int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
2973 int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
2975 if (PANEL_DEACTIVATED(game.panel.time))
2978 /* clear background if value just changed its size */
2979 if (value == 999 || value == 1000)
2980 ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
2983 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
2985 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
2988 void DrawGameValue_Level(int value)
2990 int font1_nr = FONT_TEXT_2;
2992 int font2_nr = FONT_TEXT_1;
2994 int font2_nr = FONT_LEVEL_NUMBER;
2997 if (PANEL_DEACTIVATED(game.panel.level))
3001 DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
3003 DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
3006 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
3008 int base_key_graphic = EL_KEY_1;
3011 if (PANEL_DEACTIVATED(game.panel.keys))
3014 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3015 base_key_graphic = EL_EM_KEY_1;
3017 /* currently only 4 of 8 possible keys are displayed */
3018 for (i = 0; i < STD_NUM_KEYS; i++)
3020 int x = XX_KEYS + i * MINI_TILEX;
3024 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3026 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3027 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3033 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3036 int key[MAX_NUM_KEYS];
3039 /* prevent EM engine from updating time/score values parallel to GameWon() */
3040 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3041 local_player->LevelSolved)
3044 for (i = 0; i < MAX_NUM_KEYS; i++)
3045 key[i] = key_bits & (1 << i);
3047 DrawGameValue_Level(level_nr);
3049 DrawGameValue_Emeralds(emeralds);
3050 DrawGameValue_Dynamite(dynamite);
3051 DrawGameValue_Score(score);
3052 DrawGameValue_Time(time);
3054 DrawGameValue_Keys(key);
3057 void UpdateGameDoorValues()
3059 UpdateGameControlValues();
3062 void DrawGameDoorValues()
3064 DisplayGameControlValues();
3067 void DrawGameDoorValues_OLD()
3069 int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
3070 int dynamite_value = 0;
3071 int score_value = (local_player->LevelSolved ? local_player->score_final :
3072 local_player->score);
3073 int gems_value = local_player->gems_still_needed;
3077 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3079 DrawGameDoorValues_EM();
3084 if (game.centered_player_nr == -1)
3086 for (i = 0; i < MAX_PLAYERS; i++)
3088 for (j = 0; j < MAX_NUM_KEYS; j++)
3089 if (stored_player[i].key[j])
3090 key_bits |= (1 << j);
3092 dynamite_value += stored_player[i].inventory_size;
3097 int player_nr = game.centered_player_nr;
3099 for (i = 0; i < MAX_NUM_KEYS; i++)
3100 if (stored_player[player_nr].key[i])
3101 key_bits |= (1 << i);
3103 dynamite_value = stored_player[player_nr].inventory_size;
3106 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3112 =============================================================================
3114 -----------------------------------------------------------------------------
3115 initialize game engine due to level / tape version number
3116 =============================================================================
3119 static void InitGameEngine()
3121 int i, j, k, l, x, y;
3123 /* set game engine from tape file when re-playing, else from level file */
3124 game.engine_version = (tape.playing ? tape.engine_version :
3125 level.game_version);
3127 /* ---------------------------------------------------------------------- */
3128 /* set flags for bugs and changes according to active game engine version */
3129 /* ---------------------------------------------------------------------- */
3132 Summary of bugfix/change:
3133 Fixed handling for custom elements that change when pushed by the player.
3135 Fixed/changed in version:
3139 Before 3.1.0, custom elements that "change when pushing" changed directly
3140 after the player started pushing them (until then handled in "DigField()").
3141 Since 3.1.0, these custom elements are not changed until the "pushing"
3142 move of the element is finished (now handled in "ContinueMoving()").
3144 Affected levels/tapes:
3145 The first condition is generally needed for all levels/tapes before version
3146 3.1.0, which might use the old behaviour before it was changed; known tapes
3147 that are affected are some tapes from the level set "Walpurgis Gardens" by
3149 The second condition is an exception from the above case and is needed for
3150 the special case of tapes recorded with game (not engine!) version 3.1.0 or
3151 above (including some development versions of 3.1.0), but before it was
3152 known that this change would break tapes like the above and was fixed in
3153 3.1.1, so that the changed behaviour was active although the engine version
3154 while recording maybe was before 3.1.0. There is at least one tape that is
3155 affected by this exception, which is the tape for the one-level set "Bug
3156 Machine" by Juergen Bonhagen.
3159 game.use_change_when_pushing_bug =
3160 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3162 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3163 tape.game_version < VERSION_IDENT(3,1,1,0)));
3166 Summary of bugfix/change:
3167 Fixed handling for blocking the field the player leaves when moving.
3169 Fixed/changed in version:
3173 Before 3.1.1, when "block last field when moving" was enabled, the field
3174 the player is leaving when moving was blocked for the time of the move,
3175 and was directly unblocked afterwards. This resulted in the last field
3176 being blocked for exactly one less than the number of frames of one player
3177 move. Additionally, even when blocking was disabled, the last field was
3178 blocked for exactly one frame.
3179 Since 3.1.1, due to changes in player movement handling, the last field
3180 is not blocked at all when blocking is disabled. When blocking is enabled,
3181 the last field is blocked for exactly the number of frames of one player
3182 move. Additionally, if the player is Murphy, the hero of Supaplex, the
3183 last field is blocked for exactly one more than the number of frames of
3186 Affected levels/tapes:
3187 (!!! yet to be determined -- probably many !!!)
3190 game.use_block_last_field_bug =
3191 (game.engine_version < VERSION_IDENT(3,1,1,0));
3194 Summary of bugfix/change:
3195 Changed behaviour of CE changes with multiple changes per single frame.
3197 Fixed/changed in version:
3201 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3202 This resulted in race conditions where CEs seem to behave strange in some
3203 situations (where triggered CE changes were just skipped because there was
3204 already a CE change on that tile in the playfield in that engine frame).
3205 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3206 (The number of changes per frame must be limited in any case, because else
3207 it is easily possible to define CE changes that would result in an infinite
3208 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3209 should be set large enough so that it would only be reached in cases where
3210 the corresponding CE change conditions run into a loop. Therefore, it seems
3211 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3212 maximal number of change pages for custom elements.)
3214 Affected levels/tapes:
3218 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3219 game.max_num_changes_per_frame = 1;
3221 game.max_num_changes_per_frame =
3222 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3225 /* ---------------------------------------------------------------------- */
3227 /* default scan direction: scan playfield from top/left to bottom/right */
3228 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3230 /* dynamically adjust element properties according to game engine version */
3231 InitElementPropertiesEngine(game.engine_version);
3234 printf("level %d: level version == %06d\n", level_nr, level.game_version);
3235 printf(" tape version == %06d [%s] [file: %06d]\n",
3236 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3238 printf(" => game.engine_version == %06d\n", game.engine_version);
3241 /* ---------- initialize player's initial move delay --------------------- */
3243 /* dynamically adjust player properties according to level information */
3244 for (i = 0; i < MAX_PLAYERS; i++)
3245 game.initial_move_delay_value[i] =
3246 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3248 /* dynamically adjust player properties according to game engine version */
3249 for (i = 0; i < MAX_PLAYERS; i++)
3250 game.initial_move_delay[i] =
3251 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3252 game.initial_move_delay_value[i] : 0);
3254 /* ---------- initialize player's initial push delay --------------------- */
3256 /* dynamically adjust player properties according to game engine version */
3257 game.initial_push_delay_value =
3258 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3260 /* ---------- initialize changing elements ------------------------------- */
3262 /* initialize changing elements information */
3263 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3265 struct ElementInfo *ei = &element_info[i];
3267 /* this pointer might have been changed in the level editor */
3268 ei->change = &ei->change_page[0];
3270 if (!IS_CUSTOM_ELEMENT(i))
3272 ei->change->target_element = EL_EMPTY_SPACE;
3273 ei->change->delay_fixed = 0;
3274 ei->change->delay_random = 0;
3275 ei->change->delay_frames = 1;
3278 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3280 ei->has_change_event[j] = FALSE;
3282 ei->event_page_nr[j] = 0;
3283 ei->event_page[j] = &ei->change_page[0];
3287 /* add changing elements from pre-defined list */
3288 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3290 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3291 struct ElementInfo *ei = &element_info[ch_delay->element];
3293 ei->change->target_element = ch_delay->target_element;
3294 ei->change->delay_fixed = ch_delay->change_delay;
3296 ei->change->pre_change_function = ch_delay->pre_change_function;
3297 ei->change->change_function = ch_delay->change_function;
3298 ei->change->post_change_function = ch_delay->post_change_function;
3300 ei->change->can_change = TRUE;
3301 ei->change->can_change_or_has_action = TRUE;
3303 ei->has_change_event[CE_DELAY] = TRUE;
3305 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3306 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3309 /* ---------- initialize internal run-time variables ------------- */
3311 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3313 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3315 for (j = 0; j < ei->num_change_pages; j++)
3317 ei->change_page[j].can_change_or_has_action =
3318 (ei->change_page[j].can_change |
3319 ei->change_page[j].has_action);
3323 /* add change events from custom element configuration */
3324 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3326 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3328 for (j = 0; j < ei->num_change_pages; j++)
3330 if (!ei->change_page[j].can_change_or_has_action)
3333 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3335 /* only add event page for the first page found with this event */
3336 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3338 ei->has_change_event[k] = TRUE;
3340 ei->event_page_nr[k] = j;
3341 ei->event_page[k] = &ei->change_page[j];
3347 /* ---------- initialize run-time trigger player and element ------------- */
3349 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3351 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3353 for (j = 0; j < ei->num_change_pages; j++)
3355 ei->change_page[j].actual_trigger_element = EL_EMPTY;
3356 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
3357 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_1;
3358 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3359 ei->change_page[j].actual_trigger_ce_value = 0;
3360 ei->change_page[j].actual_trigger_ce_score = 0;
3364 /* ---------- initialize trigger events ---------------------------------- */
3366 /* initialize trigger events information */
3367 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3368 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3369 trigger_events[i][j] = FALSE;
3371 /* add trigger events from element change event properties */
3372 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3374 struct ElementInfo *ei = &element_info[i];
3376 for (j = 0; j < ei->num_change_pages; j++)
3378 if (!ei->change_page[j].can_change_or_has_action)
3381 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3383 int trigger_element = ei->change_page[j].trigger_element;
3385 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3387 if (ei->change_page[j].has_event[k])
3389 if (IS_GROUP_ELEMENT(trigger_element))
3391 struct ElementGroupInfo *group =
3392 element_info[trigger_element].group;
3394 for (l = 0; l < group->num_elements_resolved; l++)
3395 trigger_events[group->element_resolved[l]][k] = TRUE;
3397 else if (trigger_element == EL_ANY_ELEMENT)
3398 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3399 trigger_events[l][k] = TRUE;
3401 trigger_events[trigger_element][k] = TRUE;
3408 /* ---------- initialize push delay -------------------------------------- */
3410 /* initialize push delay values to default */
3411 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3413 if (!IS_CUSTOM_ELEMENT(i))
3415 /* set default push delay values (corrected since version 3.0.7-1) */
3416 if (game.engine_version < VERSION_IDENT(3,0,7,1))
3418 element_info[i].push_delay_fixed = 2;
3419 element_info[i].push_delay_random = 8;
3423 element_info[i].push_delay_fixed = 8;
3424 element_info[i].push_delay_random = 8;
3429 /* set push delay value for certain elements from pre-defined list */
3430 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3432 int e = push_delay_list[i].element;
3434 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
3435 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3438 /* set push delay value for Supaplex elements for newer engine versions */
3439 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3441 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3443 if (IS_SP_ELEMENT(i))
3445 /* set SP push delay to just enough to push under a falling zonk */
3446 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3448 element_info[i].push_delay_fixed = delay;
3449 element_info[i].push_delay_random = 0;
3454 /* ---------- initialize move stepsize ----------------------------------- */
3456 /* initialize move stepsize values to default */
3457 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3458 if (!IS_CUSTOM_ELEMENT(i))
3459 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3461 /* set move stepsize value for certain elements from pre-defined list */
3462 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3464 int e = move_stepsize_list[i].element;
3466 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3469 /* ---------- initialize collect score ----------------------------------- */
3471 /* initialize collect score values for custom elements from initial value */
3472 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3473 if (IS_CUSTOM_ELEMENT(i))
3474 element_info[i].collect_score = element_info[i].collect_score_initial;
3476 /* ---------- initialize collect count ----------------------------------- */
3478 /* initialize collect count values for non-custom elements */
3479 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3480 if (!IS_CUSTOM_ELEMENT(i))
3481 element_info[i].collect_count_initial = 0;
3483 /* add collect count values for all elements from pre-defined list */
3484 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3485 element_info[collect_count_list[i].element].collect_count_initial =
3486 collect_count_list[i].count;
3488 /* ---------- initialize access direction -------------------------------- */
3490 /* initialize access direction values to default (access from every side) */
3491 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3492 if (!IS_CUSTOM_ELEMENT(i))
3493 element_info[i].access_direction = MV_ALL_DIRECTIONS;
3495 /* set access direction value for certain elements from pre-defined list */
3496 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3497 element_info[access_direction_list[i].element].access_direction =
3498 access_direction_list[i].direction;
3500 /* ---------- initialize explosion content ------------------------------- */
3501 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3503 if (IS_CUSTOM_ELEMENT(i))
3506 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3508 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3510 element_info[i].content.e[x][y] =
3511 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3512 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3513 i == EL_PLAYER_3 ? EL_EMERALD :
3514 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3515 i == EL_MOLE ? EL_EMERALD_RED :
3516 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3517 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3518 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3519 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3520 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3521 i == EL_WALL_EMERALD ? EL_EMERALD :
3522 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3523 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3524 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3525 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3526 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3527 i == EL_WALL_PEARL ? EL_PEARL :
3528 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3533 /* ---------- initialize recursion detection ------------------------------ */
3534 recursion_loop_depth = 0;
3535 recursion_loop_detected = FALSE;
3536 recursion_loop_element = EL_UNDEFINED;
3538 /* ---------- initialize graphics engine ---------------------------------- */
3539 game.scroll_delay_value =
3540 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3541 setup.scroll_delay ? setup.scroll_delay_value : 0);
3542 game.scroll_delay_value =
3543 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3546 int get_num_special_action(int element, int action_first, int action_last)
3548 int num_special_action = 0;
3551 for (i = action_first; i <= action_last; i++)
3553 boolean found = FALSE;
3555 for (j = 0; j < NUM_DIRECTIONS; j++)
3556 if (el_act_dir2img(element, i, j) !=
3557 el_act_dir2img(element, ACTION_DEFAULT, j))
3561 num_special_action++;
3566 return num_special_action;
3571 =============================================================================
3573 -----------------------------------------------------------------------------
3574 initialize and start new game
3575 =============================================================================
3580 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3581 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3582 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3584 boolean do_fading = (game_status == GAME_MODE_MAIN);
3588 game_status = GAME_MODE_PLAYING;
3591 InitGameControlValues();
3593 /* don't play tapes over network */
3594 network_playing = (options.network && !tape.playing);
3596 for (i = 0; i < MAX_PLAYERS; i++)
3598 struct PlayerInfo *player = &stored_player[i];
3600 player->index_nr = i;
3601 player->index_bit = (1 << i);
3602 player->element_nr = EL_PLAYER_1 + i;
3604 player->present = FALSE;
3605 player->active = FALSE;
3606 player->killed = FALSE;
3609 player->effective_action = 0;
3610 player->programmed_action = 0;
3613 player->score_final = 0;
3615 player->gems_still_needed = level.gems_needed;
3616 player->sokobanfields_still_needed = 0;
3617 player->lights_still_needed = 0;
3618 player->friends_still_needed = 0;
3620 for (j = 0; j < MAX_NUM_KEYS; j++)
3621 player->key[j] = FALSE;
3623 player->num_white_keys = 0;
3625 player->dynabomb_count = 0;
3626 player->dynabomb_size = 1;
3627 player->dynabombs_left = 0;
3628 player->dynabomb_xl = FALSE;
3630 player->MovDir = MV_NONE;
3633 player->GfxDir = MV_NONE;
3634 player->GfxAction = ACTION_DEFAULT;
3636 player->StepFrame = 0;
3638 player->use_murphy = FALSE;
3639 player->artwork_element =
3640 (level.use_artwork_element[i] ? level.artwork_element[i] :
3641 player->element_nr);
3643 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3644 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3646 player->gravity = level.initial_player_gravity[i];
3648 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3650 player->actual_frame_counter = 0;
3652 player->step_counter = 0;
3654 player->last_move_dir = MV_NONE;
3656 player->is_active = FALSE;
3658 player->is_waiting = FALSE;
3659 player->is_moving = FALSE;
3660 player->is_auto_moving = FALSE;
3661 player->is_digging = FALSE;
3662 player->is_snapping = FALSE;
3663 player->is_collecting = FALSE;
3664 player->is_pushing = FALSE;
3665 player->is_switching = FALSE;
3666 player->is_dropping = FALSE;
3667 player->is_dropping_pressed = FALSE;
3669 player->is_bored = FALSE;
3670 player->is_sleeping = FALSE;
3672 player->frame_counter_bored = -1;
3673 player->frame_counter_sleeping = -1;
3675 player->anim_delay_counter = 0;
3676 player->post_delay_counter = 0;
3678 player->dir_waiting = MV_NONE;
3679 player->action_waiting = ACTION_DEFAULT;
3680 player->last_action_waiting = ACTION_DEFAULT;
3681 player->special_action_bored = ACTION_DEFAULT;
3682 player->special_action_sleeping = ACTION_DEFAULT;
3684 player->switch_x = -1;
3685 player->switch_y = -1;
3687 player->drop_x = -1;
3688 player->drop_y = -1;
3690 player->show_envelope = 0;
3692 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3694 player->push_delay = -1; /* initialized when pushing starts */
3695 player->push_delay_value = game.initial_push_delay_value;
3697 player->drop_delay = 0;
3698 player->drop_pressed_delay = 0;
3700 player->last_jx = -1;
3701 player->last_jy = -1;
3705 player->shield_normal_time_left = 0;
3706 player->shield_deadly_time_left = 0;
3708 player->inventory_infinite_element = EL_UNDEFINED;
3709 player->inventory_size = 0;
3711 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3712 SnapField(player, 0, 0);
3714 player->LevelSolved = FALSE;
3715 player->GameOver = FALSE;
3717 player->LevelSolved_GameWon = FALSE;
3718 player->LevelSolved_GameEnd = FALSE;
3719 player->LevelSolved_PanelOff = FALSE;
3720 player->LevelSolved_SaveTape = FALSE;
3721 player->LevelSolved_SaveScore = FALSE;
3722 player->LevelSolved_CountingTime = 0;
3723 player->LevelSolved_CountingScore = 0;
3726 network_player_action_received = FALSE;
3728 #if defined(NETWORK_AVALIABLE)
3729 /* initial null action */
3730 if (network_playing)
3731 SendToServer_MovePlayer(MV_NONE);
3740 TimeLeft = level.time;
3743 ScreenMovDir = MV_NONE;
3747 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3749 AllPlayersGone = FALSE;
3751 game.yamyam_content_nr = 0;
3752 game.robot_wheel_active = FALSE;
3753 game.magic_wall_active = FALSE;
3754 game.magic_wall_time_left = 0;
3755 game.light_time_left = 0;
3756 game.timegate_time_left = 0;
3757 game.switchgate_pos = 0;
3758 game.wind_direction = level.wind_direction_initial;
3760 #if !USE_PLAYER_GRAVITY
3761 game.gravity = FALSE;
3762 game.explosions_delayed = TRUE;
3765 game.lenses_time_left = 0;
3766 game.magnify_time_left = 0;
3768 game.ball_state = level.ball_state_initial;
3769 game.ball_content_nr = 0;
3771 game.envelope_active = FALSE;
3773 /* set focus to local player for network games, else to all players */
3774 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3775 game.centered_player_nr_next = game.centered_player_nr;
3776 game.set_centered_player = FALSE;
3778 if (network_playing && tape.recording)
3780 /* store client dependent player focus when recording network games */
3781 tape.centered_player_nr_next = game.centered_player_nr_next;
3782 tape.set_centered_player = TRUE;
3785 for (i = 0; i < NUM_BELTS; i++)
3787 game.belt_dir[i] = MV_NONE;
3788 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3791 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3792 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3794 SCAN_PLAYFIELD(x, y)
3796 Feld[x][y] = level.field[x][y];
3797 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3798 ChangeDelay[x][y] = 0;
3799 ChangePage[x][y] = -1;
3800 #if USE_NEW_CUSTOM_VALUE
3801 CustomValue[x][y] = 0; /* initialized in InitField() */
3803 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3805 WasJustMoving[x][y] = 0;
3806 WasJustFalling[x][y] = 0;
3807 CheckCollision[x][y] = 0;
3808 CheckImpact[x][y] = 0;
3810 Pushed[x][y] = FALSE;
3812 ChangeCount[x][y] = 0;
3813 ChangeEvent[x][y] = -1;
3815 ExplodePhase[x][y] = 0;
3816 ExplodeDelay[x][y] = 0;
3817 ExplodeField[x][y] = EX_TYPE_NONE;
3819 RunnerVisit[x][y] = 0;
3820 PlayerVisit[x][y] = 0;
3823 GfxRandom[x][y] = INIT_GFX_RANDOM();
3824 GfxElement[x][y] = EL_UNDEFINED;
3825 GfxAction[x][y] = ACTION_DEFAULT;
3826 GfxDir[x][y] = MV_NONE;
3829 SCAN_PLAYFIELD(x, y)
3831 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3833 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3835 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3838 InitField(x, y, TRUE);
3840 ResetGfxAnimation(x, y);
3845 for (i = 0; i < MAX_PLAYERS; i++)
3847 struct PlayerInfo *player = &stored_player[i];
3849 /* set number of special actions for bored and sleeping animation */
3850 player->num_special_action_bored =
3851 get_num_special_action(player->artwork_element,
3852 ACTION_BORING_1, ACTION_BORING_LAST);
3853 player->num_special_action_sleeping =
3854 get_num_special_action(player->artwork_element,
3855 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3858 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3859 emulate_sb ? EMU_SOKOBAN :
3860 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3862 #if USE_NEW_ALL_SLIPPERY
3863 /* initialize type of slippery elements */
3864 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3866 if (!IS_CUSTOM_ELEMENT(i))
3868 /* default: elements slip down either to the left or right randomly */
3869 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3871 /* SP style elements prefer to slip down on the left side */
3872 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3873 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3875 /* BD style elements prefer to slip down on the left side */
3876 if (game.emulation == EMU_BOULDERDASH)
3877 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3882 /* initialize explosion and ignition delay */
3883 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3885 if (!IS_CUSTOM_ELEMENT(i))
3888 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3889 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3890 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3891 int last_phase = (num_phase + 1) * delay;
3892 int half_phase = (num_phase / 2) * delay;
3894 element_info[i].explosion_delay = last_phase - 1;
3895 element_info[i].ignition_delay = half_phase;
3897 if (i == EL_BLACK_ORB)
3898 element_info[i].ignition_delay = 1;
3902 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
3903 element_info[i].explosion_delay = 1;
3905 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
3906 element_info[i].ignition_delay = 1;
3910 /* correct non-moving belts to start moving left */
3911 for (i = 0; i < NUM_BELTS; i++)
3912 if (game.belt_dir[i] == MV_NONE)
3913 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3915 /* check if any connected player was not found in playfield */
3916 for (i = 0; i < MAX_PLAYERS; i++)
3918 struct PlayerInfo *player = &stored_player[i];
3920 if (player->connected && !player->present)
3922 for (j = 0; j < MAX_PLAYERS; j++)
3924 struct PlayerInfo *some_player = &stored_player[j];
3925 int jx = some_player->jx, jy = some_player->jy;
3927 /* assign first free player found that is present in the playfield */
3928 if (some_player->present && !some_player->connected)
3930 player->present = TRUE;
3931 player->active = TRUE;
3933 some_player->present = FALSE;
3934 some_player->active = FALSE;
3936 player->artwork_element = some_player->artwork_element;
3938 player->block_last_field = some_player->block_last_field;
3939 player->block_delay_adjustment = some_player->block_delay_adjustment;
3941 StorePlayer[jx][jy] = player->element_nr;
3942 player->jx = player->last_jx = jx;
3943 player->jy = player->last_jy = jy;
3953 /* when playing a tape, eliminate all players who do not participate */
3955 for (i = 0; i < MAX_PLAYERS; i++)
3957 if (stored_player[i].active && !tape.player_participates[i])
3959 struct PlayerInfo *player = &stored_player[i];
3960 int jx = player->jx, jy = player->jy;
3962 player->active = FALSE;
3963 StorePlayer[jx][jy] = 0;
3964 Feld[jx][jy] = EL_EMPTY;
3968 else if (!options.network && !setup.team_mode) /* && !tape.playing */
3970 /* when in single player mode, eliminate all but the first active player */
3972 for (i = 0; i < MAX_PLAYERS; i++)
3974 if (stored_player[i].active)
3976 for (j = i + 1; j < MAX_PLAYERS; j++)
3978 if (stored_player[j].active)
3980 struct PlayerInfo *player = &stored_player[j];
3981 int jx = player->jx, jy = player->jy;
3983 player->active = FALSE;
3984 player->present = FALSE;
3986 StorePlayer[jx][jy] = 0;
3987 Feld[jx][jy] = EL_EMPTY;
3994 /* when recording the game, store which players take part in the game */
3997 for (i = 0; i < MAX_PLAYERS; i++)
3998 if (stored_player[i].active)
3999 tape.player_participates[i] = TRUE;
4004 for (i = 0; i < MAX_PLAYERS; i++)
4006 struct PlayerInfo *player = &stored_player[i];
4008 printf("Player %d: present == %d, connected == %d, active == %d.\n",
4013 if (local_player == player)
4014 printf("Player %d is local player.\n", i+1);
4018 if (BorderElement == EL_EMPTY)
4021 SBX_Right = lev_fieldx - SCR_FIELDX;
4023 SBY_Lower = lev_fieldy - SCR_FIELDY;
4028 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4030 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4033 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4034 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4036 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4037 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4039 /* if local player not found, look for custom element that might create
4040 the player (make some assumptions about the right custom element) */
4041 if (!local_player->present)
4043 int start_x = 0, start_y = 0;
4044 int found_rating = 0;
4045 int found_element = EL_UNDEFINED;
4046 int player_nr = local_player->index_nr;
4048 SCAN_PLAYFIELD(x, y)
4050 int element = Feld[x][y];
4055 if (level.use_start_element[player_nr] &&
4056 level.start_element[player_nr] == element &&
4063 found_element = element;
4066 if (!IS_CUSTOM_ELEMENT(element))
4069 if (CAN_CHANGE(element))
4071 for (i = 0; i < element_info[element].num_change_pages; i++)
4073 /* check for player created from custom element as single target */
4074 content = element_info[element].change_page[i].target_element;
4075 is_player = ELEM_IS_PLAYER(content);
4077 if (is_player && (found_rating < 3 ||
4078 (found_rating == 3 && element < found_element)))
4084 found_element = element;
4089 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4091 /* check for player created from custom element as explosion content */
4092 content = element_info[element].content.e[xx][yy];
4093 is_player = ELEM_IS_PLAYER(content);
4095 if (is_player && (found_rating < 2 ||
4096 (found_rating == 2 && element < found_element)))
4098 start_x = x + xx - 1;
4099 start_y = y + yy - 1;
4102 found_element = element;
4105 if (!CAN_CHANGE(element))
4108 for (i = 0; i < element_info[element].num_change_pages; i++)
4110 /* check for player created from custom element as extended target */
4112 element_info[element].change_page[i].target_content.e[xx][yy];
4114 is_player = ELEM_IS_PLAYER(content);
4116 if (is_player && (found_rating < 1 ||
4117 (found_rating == 1 && element < found_element)))
4119 start_x = x + xx - 1;
4120 start_y = y + yy - 1;
4123 found_element = element;
4129 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
4130 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4133 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4134 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4139 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
4140 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4141 local_player->jx - MIDPOSX);
4143 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4144 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4145 local_player->jy - MIDPOSY);
4149 /* do not use PLAYING mask for fading out from main screen */
4150 game_status = GAME_MODE_MAIN;
4155 if (!game.restart_level)
4156 CloseDoor(DOOR_CLOSE_1);
4159 if (level_editor_test_game)
4160 FadeSkipNextFadeIn();
4162 FadeSetEnterScreen();
4164 if (level_editor_test_game)
4165 fading = fading_none;
4167 fading = menu.destination;
4171 FadeOut(REDRAW_FIELD);
4174 FadeOut(REDRAW_FIELD);
4178 game_status = GAME_MODE_PLAYING;
4181 /* !!! FIX THIS (START) !!! */
4182 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4184 InitGameEngine_EM();
4186 /* blit playfield from scroll buffer to normal back buffer for fading in */
4187 BlitScreenToBitmap_EM(backbuffer);
4194 /* after drawing the level, correct some elements */
4195 if (game.timegate_time_left == 0)
4196 CloseAllOpenTimegates();
4198 /* blit playfield from scroll buffer to normal back buffer for fading in */
4199 if (setup.soft_scrolling)
4200 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4202 redraw_mask |= REDRAW_FROM_BACKBUFFER;
4204 /* !!! FIX THIS (END) !!! */
4207 FadeIn(REDRAW_FIELD);
4210 FadeIn(REDRAW_FIELD);
4215 if (!game.restart_level)
4217 /* copy default game door content to main double buffer */
4218 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4219 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4222 SetPanelBackground();
4223 SetDrawBackgroundMask(REDRAW_DOOR_1);
4226 UpdateAndDisplayGameControlValues();
4228 UpdateGameDoorValues();
4229 DrawGameDoorValues();
4232 if (!game.restart_level)
4236 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4237 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4238 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4242 /* copy actual game door content to door double buffer for OpenDoor() */
4243 BlitBitmap(drawto, bitmap_db_door,
4244 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4246 OpenDoor(DOOR_OPEN_ALL);
4248 PlaySound(SND_GAME_STARTING);
4250 if (setup.sound_music)
4253 KeyboardAutoRepeatOffUnlessAutoplay();
4257 for (i = 0; i < MAX_PLAYERS; i++)
4258 printf("Player %d %sactive.\n",
4259 i + 1, (stored_player[i].active ? "" : "not "));
4270 game.restart_level = FALSE;
4273 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4275 /* this is used for non-R'n'D game engines to update certain engine values */
4277 /* needed to determine if sounds are played within the visible screen area */
4278 scroll_x = actual_scroll_x;
4279 scroll_y = actual_scroll_y;
4282 void InitMovDir(int x, int y)
4284 int i, element = Feld[x][y];
4285 static int xy[4][2] =
4292 static int direction[3][4] =
4294 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4295 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4296 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4305 Feld[x][y] = EL_BUG;
4306 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4309 case EL_SPACESHIP_RIGHT:
4310 case EL_SPACESHIP_UP:
4311 case EL_SPACESHIP_LEFT:
4312 case EL_SPACESHIP_DOWN:
4313 Feld[x][y] = EL_SPACESHIP;
4314 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4317 case EL_BD_BUTTERFLY_RIGHT:
4318 case EL_BD_BUTTERFLY_UP:
4319 case EL_BD_BUTTERFLY_LEFT:
4320 case EL_BD_BUTTERFLY_DOWN:
4321 Feld[x][y] = EL_BD_BUTTERFLY;
4322 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4325 case EL_BD_FIREFLY_RIGHT:
4326 case EL_BD_FIREFLY_UP:
4327 case EL_BD_FIREFLY_LEFT:
4328 case EL_BD_FIREFLY_DOWN:
4329 Feld[x][y] = EL_BD_FIREFLY;
4330 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4333 case EL_PACMAN_RIGHT:
4335 case EL_PACMAN_LEFT:
4336 case EL_PACMAN_DOWN:
4337 Feld[x][y] = EL_PACMAN;
4338 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4341 case EL_YAMYAM_LEFT:
4342 case EL_YAMYAM_RIGHT:
4344 case EL_YAMYAM_DOWN:
4345 Feld[x][y] = EL_YAMYAM;
4346 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4349 case EL_SP_SNIKSNAK:
4350 MovDir[x][y] = MV_UP;
4353 case EL_SP_ELECTRON:
4354 MovDir[x][y] = MV_LEFT;
4361 Feld[x][y] = EL_MOLE;
4362 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4366 if (IS_CUSTOM_ELEMENT(element))
4368 struct ElementInfo *ei = &element_info[element];
4369 int move_direction_initial = ei->move_direction_initial;
4370 int move_pattern = ei->move_pattern;
4372 if (move_direction_initial == MV_START_PREVIOUS)
4374 if (MovDir[x][y] != MV_NONE)
4377 move_direction_initial = MV_START_AUTOMATIC;
4380 if (move_direction_initial == MV_START_RANDOM)
4381 MovDir[x][y] = 1 << RND(4);
4382 else if (move_direction_initial & MV_ANY_DIRECTION)
4383 MovDir[x][y] = move_direction_initial;
4384 else if (move_pattern == MV_ALL_DIRECTIONS ||
4385 move_pattern == MV_TURNING_LEFT ||
4386 move_pattern == MV_TURNING_RIGHT ||
4387 move_pattern == MV_TURNING_LEFT_RIGHT ||
4388 move_pattern == MV_TURNING_RIGHT_LEFT ||
4389 move_pattern == MV_TURNING_RANDOM)
4390 MovDir[x][y] = 1 << RND(4);
4391 else if (move_pattern == MV_HORIZONTAL)
4392 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4393 else if (move_pattern == MV_VERTICAL)
4394 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4395 else if (move_pattern & MV_ANY_DIRECTION)
4396 MovDir[x][y] = element_info[element].move_pattern;
4397 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4398 move_pattern == MV_ALONG_RIGHT_SIDE)
4400 /* use random direction as default start direction */
4401 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4402 MovDir[x][y] = 1 << RND(4);
4404 for (i = 0; i < NUM_DIRECTIONS; i++)
4406 int x1 = x + xy[i][0];
4407 int y1 = y + xy[i][1];
4409 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4411 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4412 MovDir[x][y] = direction[0][i];
4414 MovDir[x][y] = direction[1][i];
4423 MovDir[x][y] = 1 << RND(4);
4425 if (element != EL_BUG &&
4426 element != EL_SPACESHIP &&
4427 element != EL_BD_BUTTERFLY &&
4428 element != EL_BD_FIREFLY)
4431 for (i = 0; i < NUM_DIRECTIONS; i++)
4433 int x1 = x + xy[i][0];
4434 int y1 = y + xy[i][1];
4436 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4438 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4440 MovDir[x][y] = direction[0][i];
4443 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4444 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4446 MovDir[x][y] = direction[1][i];
4455 GfxDir[x][y] = MovDir[x][y];
4458 void InitAmoebaNr(int x, int y)
4461 int group_nr = AmoebeNachbarNr(x, y);
4465 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4467 if (AmoebaCnt[i] == 0)
4475 AmoebaNr[x][y] = group_nr;
4476 AmoebaCnt[group_nr]++;
4477 AmoebaCnt2[group_nr]++;
4480 static void PlayerWins(struct PlayerInfo *player)
4482 player->LevelSolved = TRUE;
4483 player->GameOver = TRUE;
4485 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4486 level.native_em_level->lev->score : player->score);
4488 player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4489 player->LevelSolved_CountingScore = player->score_final;
4494 static int time, time_final;
4495 static int score, score_final;
4496 static int game_over_delay_1 = 0;
4497 static int game_over_delay_2 = 0;
4498 int game_over_delay_value_1 = 50;
4499 int game_over_delay_value_2 = 50;
4501 if (!local_player->LevelSolved_GameWon)
4505 /* do not start end game actions before the player stops moving (to exit) */
4506 if (local_player->MovPos)
4509 local_player->LevelSolved_GameWon = TRUE;
4510 local_player->LevelSolved_SaveTape = tape.recording;
4511 local_player->LevelSolved_SaveScore = !tape.playing;
4513 if (tape.auto_play) /* tape might already be stopped here */
4514 tape.auto_play_level_solved = TRUE;
4520 game_over_delay_1 = game_over_delay_value_1;
4521 game_over_delay_2 = game_over_delay_value_2;
4523 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4524 score = score_final = local_player->score_final;
4529 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4531 else if (level.time == 0 && TimePlayed < 999)
4534 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4537 local_player->score_final = score_final;
4539 if (level_editor_test_game)
4542 score = score_final;
4545 local_player->LevelSolved_CountingTime = time;
4546 local_player->LevelSolved_CountingScore = score;
4548 game_panel_controls[GAME_PANEL_TIME].value = time;
4549 game_panel_controls[GAME_PANEL_SCORE].value = score;
4551 DisplayGameControlValues();
4553 DrawGameValue_Time(time);
4554 DrawGameValue_Score(score);
4558 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4560 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4562 /* close exit door after last player */
4563 if ((AllPlayersGone &&
4564 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4565 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4566 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4567 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4568 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4570 int element = Feld[ExitX][ExitY];
4573 if (element == EL_EM_EXIT_OPEN ||
4574 element == EL_EM_STEEL_EXIT_OPEN)
4581 Feld[ExitX][ExitY] =
4582 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4583 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4584 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4585 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4586 EL_EM_STEEL_EXIT_CLOSING);
4588 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4592 /* player disappears */
4593 DrawLevelField(ExitX, ExitY);
4596 for (i = 0; i < MAX_PLAYERS; i++)
4598 struct PlayerInfo *player = &stored_player[i];
4600 if (player->present)
4602 RemovePlayer(player);
4604 /* player disappears */
4605 DrawLevelField(player->jx, player->jy);
4610 PlaySound(SND_GAME_WINNING);
4613 if (game_over_delay_1 > 0)
4615 game_over_delay_1--;
4620 if (time != time_final)
4622 int time_to_go = ABS(time_final - time);
4623 int time_count_dir = (time < time_final ? +1 : -1);
4624 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4626 time += time_count_steps * time_count_dir;
4627 score += time_count_steps * level.score[SC_TIME_BONUS];
4630 local_player->LevelSolved_CountingTime = time;
4631 local_player->LevelSolved_CountingScore = score;
4633 game_panel_controls[GAME_PANEL_TIME].value = time;
4634 game_panel_controls[GAME_PANEL_SCORE].value = score;
4636 DisplayGameControlValues();
4638 DrawGameValue_Time(time);
4639 DrawGameValue_Score(score);
4642 if (time == time_final)
4643 StopSound(SND_GAME_LEVELTIME_BONUS);
4644 else if (setup.sound_loops)
4645 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4647 PlaySound(SND_GAME_LEVELTIME_BONUS);
4652 local_player->LevelSolved_PanelOff = TRUE;
4654 if (game_over_delay_2 > 0)
4656 game_over_delay_2--;
4669 boolean raise_level = FALSE;
4671 local_player->LevelSolved_GameEnd = TRUE;
4673 CloseDoor(DOOR_CLOSE_1);
4675 if (local_player->LevelSolved_SaveTape)
4682 SaveTapeChecked(tape.level_nr); /* ask to save tape */
4684 SaveTape(tape.level_nr); /* ask to save tape */
4688 if (level_editor_test_game)
4690 game_status = GAME_MODE_MAIN;
4693 DrawAndFadeInMainMenu(REDRAW_FIELD);
4701 if (!local_player->LevelSolved_SaveScore)
4704 FadeOut(REDRAW_FIELD);
4707 game_status = GAME_MODE_MAIN;
4709 DrawAndFadeInMainMenu(REDRAW_FIELD);
4714 if (level_nr == leveldir_current->handicap_level)
4716 leveldir_current->handicap_level++;
4717 SaveLevelSetup_SeriesInfo();
4720 if (level_nr < leveldir_current->last_level)
4721 raise_level = TRUE; /* advance to next level */
4723 if ((hi_pos = NewHiScore()) >= 0)
4725 game_status = GAME_MODE_SCORES;
4727 DrawHallOfFame(hi_pos);
4738 FadeOut(REDRAW_FIELD);
4741 game_status = GAME_MODE_MAIN;
4749 DrawAndFadeInMainMenu(REDRAW_FIELD);
4758 LoadScore(level_nr);
4760 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4761 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4764 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4766 if (local_player->score_final > highscore[k].Score)
4768 /* player has made it to the hall of fame */
4770 if (k < MAX_SCORE_ENTRIES - 1)
4772 int m = MAX_SCORE_ENTRIES - 1;
4775 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4776 if (strEqual(setup.player_name, highscore[l].Name))
4778 if (m == k) /* player's new highscore overwrites his old one */
4782 for (l = m; l > k; l--)
4784 strcpy(highscore[l].Name, highscore[l - 1].Name);
4785 highscore[l].Score = highscore[l - 1].Score;
4792 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4793 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4794 highscore[k].Score = local_player->score_final;
4800 else if (!strncmp(setup.player_name, highscore[k].Name,
4801 MAX_PLAYER_NAME_LEN))
4802 break; /* player already there with a higher score */
4808 SaveScore(level_nr);
4813 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4815 int element = Feld[x][y];
4816 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4817 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4818 int horiz_move = (dx != 0);
4819 int sign = (horiz_move ? dx : dy);
4820 int step = sign * element_info[element].move_stepsize;
4822 /* special values for move stepsize for spring and things on conveyor belt */
4825 if (CAN_FALL(element) &&
4826 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4827 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4828 else if (element == EL_SPRING)
4829 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4835 inline static int getElementMoveStepsize(int x, int y)
4837 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4840 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4842 if (player->GfxAction != action || player->GfxDir != dir)
4845 printf("Player frame reset! (%d => %d, %d => %d)\n",
4846 player->GfxAction, action, player->GfxDir, dir);
4849 player->GfxAction = action;
4850 player->GfxDir = dir;
4852 player->StepFrame = 0;
4856 #if USE_GFX_RESET_GFX_ANIMATION
4857 static void ResetGfxFrame(int x, int y, boolean redraw)
4859 int element = Feld[x][y];
4860 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4861 int last_gfx_frame = GfxFrame[x][y];
4863 if (graphic_info[graphic].anim_global_sync)
4864 GfxFrame[x][y] = FrameCounter;
4865 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4866 GfxFrame[x][y] = CustomValue[x][y];
4867 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4868 GfxFrame[x][y] = element_info[element].collect_score;
4869 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4870 GfxFrame[x][y] = ChangeDelay[x][y];
4872 if (redraw && GfxFrame[x][y] != last_gfx_frame)
4873 DrawLevelGraphicAnimation(x, y, graphic);
4877 static void ResetGfxAnimation(int x, int y)
4879 GfxAction[x][y] = ACTION_DEFAULT;
4880 GfxDir[x][y] = MovDir[x][y];
4883 #if USE_GFX_RESET_GFX_ANIMATION
4884 ResetGfxFrame(x, y, FALSE);
4888 static void ResetRandomAnimationValue(int x, int y)
4890 GfxRandom[x][y] = INIT_GFX_RANDOM();
4893 void InitMovingField(int x, int y, int direction)
4895 int element = Feld[x][y];
4896 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4897 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4900 boolean is_moving_before, is_moving_after;
4902 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
4905 /* check if element was/is moving or being moved before/after mode change */
4908 is_moving_before = (WasJustMoving[x][y] != 0);
4910 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
4911 is_moving_before = WasJustMoving[x][y];
4914 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
4916 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
4918 /* reset animation only for moving elements which change direction of moving
4919 or which just started or stopped moving
4920 (else CEs with property "can move" / "not moving" are reset each frame) */
4921 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4923 if (is_moving_before != is_moving_after ||
4924 direction != MovDir[x][y])
4925 ResetGfxAnimation(x, y);
4927 if ((is_moving_before || is_moving_after) && !continues_moving)
4928 ResetGfxAnimation(x, y);
4931 if (!continues_moving)
4932 ResetGfxAnimation(x, y);
4935 MovDir[x][y] = direction;
4936 GfxDir[x][y] = direction;
4938 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4939 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4940 direction == MV_DOWN && CAN_FALL(element) ?
4941 ACTION_FALLING : ACTION_MOVING);
4943 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
4944 ACTION_FALLING : ACTION_MOVING);
4947 /* this is needed for CEs with property "can move" / "not moving" */
4949 if (is_moving_after)
4951 if (Feld[newx][newy] == EL_EMPTY)
4952 Feld[newx][newy] = EL_BLOCKED;
4954 MovDir[newx][newy] = MovDir[x][y];
4956 #if USE_NEW_CUSTOM_VALUE
4957 CustomValue[newx][newy] = CustomValue[x][y];
4960 GfxFrame[newx][newy] = GfxFrame[x][y];
4961 GfxRandom[newx][newy] = GfxRandom[x][y];
4962 GfxAction[newx][newy] = GfxAction[x][y];
4963 GfxDir[newx][newy] = GfxDir[x][y];
4967 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4969 int direction = MovDir[x][y];
4970 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4971 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
4977 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4979 int oldx = x, oldy = y;
4980 int direction = MovDir[x][y];
4982 if (direction == MV_LEFT)
4984 else if (direction == MV_RIGHT)
4986 else if (direction == MV_UP)
4988 else if (direction == MV_DOWN)
4991 *comes_from_x = oldx;
4992 *comes_from_y = oldy;
4995 int MovingOrBlocked2Element(int x, int y)
4997 int element = Feld[x][y];
4999 if (element == EL_BLOCKED)
5003 Blocked2Moving(x, y, &oldx, &oldy);
5004 return Feld[oldx][oldy];
5010 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5012 /* like MovingOrBlocked2Element(), but if element is moving
5013 and (x,y) is the field the moving element is just leaving,
5014 return EL_BLOCKED instead of the element value */
5015 int element = Feld[x][y];
5017 if (IS_MOVING(x, y))
5019 if (element == EL_BLOCKED)
5023 Blocked2Moving(x, y, &oldx, &oldy);
5024 return Feld[oldx][oldy];
5033 static void RemoveField(int x, int y)
5035 Feld[x][y] = EL_EMPTY;
5041 #if USE_NEW_CUSTOM_VALUE
5042 CustomValue[x][y] = 0;
5046 ChangeDelay[x][y] = 0;
5047 ChangePage[x][y] = -1;
5048 Pushed[x][y] = FALSE;
5051 ExplodeField[x][y] = EX_TYPE_NONE;
5054 GfxElement[x][y] = EL_UNDEFINED;
5055 GfxAction[x][y] = ACTION_DEFAULT;
5056 GfxDir[x][y] = MV_NONE;
5059 void RemoveMovingField(int x, int y)
5061 int oldx = x, oldy = y, newx = x, newy = y;
5062 int element = Feld[x][y];
5063 int next_element = EL_UNDEFINED;
5065 if (element != EL_BLOCKED && !IS_MOVING(x, y))
5068 if (IS_MOVING(x, y))
5070 Moving2Blocked(x, y, &newx, &newy);
5072 if (Feld[newx][newy] != EL_BLOCKED)
5074 /* element is moving, but target field is not free (blocked), but
5075 already occupied by something different (example: acid pool);
5076 in this case, only remove the moving field, but not the target */
5078 RemoveField(oldx, oldy);
5080 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5082 DrawLevelField(oldx, oldy);
5087 else if (element == EL_BLOCKED)
5089 Blocked2Moving(x, y, &oldx, &oldy);
5090 if (!IS_MOVING(oldx, oldy))
5094 if (element == EL_BLOCKED &&
5095 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5096 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5097 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5098 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5099 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5100 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5101 next_element = get_next_element(Feld[oldx][oldy]);
5103 RemoveField(oldx, oldy);
5104 RemoveField(newx, newy);
5106 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5108 if (next_element != EL_UNDEFINED)
5109 Feld[oldx][oldy] = next_element;
5111 DrawLevelField(oldx, oldy);
5112 DrawLevelField(newx, newy);
5115 void DrawDynamite(int x, int y)
5117 int sx = SCREENX(x), sy = SCREENY(y);
5118 int graphic = el2img(Feld[x][y]);
5121 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5124 if (IS_WALKABLE_INSIDE(Back[x][y]))
5128 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5129 else if (Store[x][y])
5130 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5132 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5134 if (Back[x][y] || Store[x][y])
5135 DrawGraphicThruMask(sx, sy, graphic, frame);
5137 DrawGraphic(sx, sy, graphic, frame);
5140 void CheckDynamite(int x, int y)
5142 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5146 if (MovDelay[x][y] != 0)
5149 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5155 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5160 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5162 boolean num_checked_players = 0;
5165 for (i = 0; i < MAX_PLAYERS; i++)
5167 if (stored_player[i].active)
5169 int sx = stored_player[i].jx;
5170 int sy = stored_player[i].jy;
5172 if (num_checked_players == 0)
5179 *sx1 = MIN(*sx1, sx);
5180 *sy1 = MIN(*sy1, sy);
5181 *sx2 = MAX(*sx2, sx);
5182 *sy2 = MAX(*sy2, sy);
5185 num_checked_players++;
5190 static boolean checkIfAllPlayersFitToScreen_RND()
5192 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5194 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5196 return (sx2 - sx1 < SCR_FIELDX &&
5197 sy2 - sy1 < SCR_FIELDY);
5200 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5202 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5204 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5206 *sx = (sx1 + sx2) / 2;
5207 *sy = (sy1 + sy2) / 2;
5210 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5211 boolean center_screen, boolean quick_relocation)
5213 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5214 boolean no_delay = (tape.warp_forward);
5215 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5216 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5218 if (quick_relocation)
5220 int offset = game.scroll_delay_value;
5222 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5224 if (!level.shifted_relocation || center_screen)
5226 /* quick relocation (without scrolling), with centering of screen */
5228 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5229 x > SBX_Right + MIDPOSX ? SBX_Right :
5232 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5233 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5238 /* quick relocation (without scrolling), but do not center screen */
5240 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5241 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5244 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5245 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5248 int offset_x = x + (scroll_x - center_scroll_x);
5249 int offset_y = y + (scroll_y - center_scroll_y);
5251 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5252 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5253 offset_x - MIDPOSX);
5255 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5256 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5257 offset_y - MIDPOSY);
5262 /* quick relocation (without scrolling), inside visible screen area */
5264 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
5265 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5266 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5268 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
5269 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5270 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5272 /* don't scroll over playfield boundaries */
5273 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5274 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5276 /* don't scroll over playfield boundaries */
5277 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5278 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5281 RedrawPlayfield(TRUE, 0,0,0,0);
5286 int scroll_xx, scroll_yy;
5288 if (!level.shifted_relocation || center_screen)
5290 /* visible relocation (with scrolling), with centering of screen */
5292 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5293 x > SBX_Right + MIDPOSX ? SBX_Right :
5296 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5297 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5302 /* visible relocation (with scrolling), but do not center screen */
5304 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5305 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5308 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5309 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5312 int offset_x = x + (scroll_x - center_scroll_x);
5313 int offset_y = y + (scroll_y - center_scroll_y);
5315 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5316 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5317 offset_x - MIDPOSX);
5319 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5320 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5321 offset_y - MIDPOSY);
5326 /* visible relocation (with scrolling), with centering of screen */
5328 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5329 x > SBX_Right + MIDPOSX ? SBX_Right :
5332 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5333 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5337 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5339 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5342 int fx = FX, fy = FY;
5344 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5345 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5347 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5353 fx += dx * TILEX / 2;
5354 fy += dy * TILEY / 2;
5356 ScrollLevel(dx, dy);
5359 /* scroll in two steps of half tile size to make things smoother */
5360 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5362 Delay(wait_delay_value);
5364 /* scroll second step to align at full tile size */
5366 Delay(wait_delay_value);
5371 Delay(wait_delay_value);
5375 void RelocatePlayer(int jx, int jy, int el_player_raw)
5377 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5378 int player_nr = GET_PLAYER_NR(el_player);
5379 struct PlayerInfo *player = &stored_player[player_nr];
5380 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5381 boolean no_delay = (tape.warp_forward);
5382 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5383 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5384 int old_jx = player->jx;
5385 int old_jy = player->jy;
5386 int old_element = Feld[old_jx][old_jy];
5387 int element = Feld[jx][jy];
5388 boolean player_relocated = (old_jx != jx || old_jy != jy);
5390 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5391 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5392 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5393 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5394 int leave_side_horiz = move_dir_horiz;
5395 int leave_side_vert = move_dir_vert;
5396 int enter_side = enter_side_horiz | enter_side_vert;
5397 int leave_side = leave_side_horiz | leave_side_vert;
5399 if (player->GameOver) /* do not reanimate dead player */
5402 if (!player_relocated) /* no need to relocate the player */
5405 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5407 RemoveField(jx, jy); /* temporarily remove newly placed player */
5408 DrawLevelField(jx, jy);
5411 if (player->present)
5413 while (player->MovPos)
5415 ScrollPlayer(player, SCROLL_GO_ON);
5416 ScrollScreen(NULL, SCROLL_GO_ON);
5418 AdvanceFrameAndPlayerCounters(player->index_nr);
5423 Delay(wait_delay_value);
5426 DrawPlayer(player); /* needed here only to cleanup last field */
5427 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5429 player->is_moving = FALSE;
5432 if (IS_CUSTOM_ELEMENT(old_element))
5433 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5435 player->index_bit, leave_side);
5437 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5439 player->index_bit, leave_side);
5441 Feld[jx][jy] = el_player;
5442 InitPlayerField(jx, jy, el_player, TRUE);
5444 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5446 Feld[jx][jy] = element;
5447 InitField(jx, jy, FALSE);
5450 /* only visually relocate centered player */
5451 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5452 FALSE, level.instant_relocation);
5454 TestIfPlayerTouchesBadThing(jx, jy);
5455 TestIfPlayerTouchesCustomElement(jx, jy);
5457 if (IS_CUSTOM_ELEMENT(element))
5458 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5459 player->index_bit, enter_side);
5461 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5462 player->index_bit, enter_side);
5465 void Explode(int ex, int ey, int phase, int mode)
5471 /* !!! eliminate this variable !!! */
5472 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5474 if (game.explosions_delayed)
5476 ExplodeField[ex][ey] = mode;
5480 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5482 int center_element = Feld[ex][ey];
5483 int artwork_element, explosion_element; /* set these values later */
5486 /* --- This is only really needed (and now handled) in "Impact()". --- */
5487 /* do not explode moving elements that left the explode field in time */
5488 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5489 center_element == EL_EMPTY &&
5490 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5495 /* !!! at this place, the center element may be EL_BLOCKED !!! */
5496 if (mode == EX_TYPE_NORMAL ||
5497 mode == EX_TYPE_CENTER ||
5498 mode == EX_TYPE_CROSS)
5499 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5502 /* remove things displayed in background while burning dynamite */
5503 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5506 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5508 /* put moving element to center field (and let it explode there) */
5509 center_element = MovingOrBlocked2Element(ex, ey);
5510 RemoveMovingField(ex, ey);
5511 Feld[ex][ey] = center_element;
5514 /* now "center_element" is finally determined -- set related values now */
5515 artwork_element = center_element; /* for custom player artwork */
5516 explosion_element = center_element; /* for custom player artwork */
5518 if (IS_PLAYER(ex, ey))
5520 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5522 artwork_element = stored_player[player_nr].artwork_element;
5524 if (level.use_explosion_element[player_nr])
5526 explosion_element = level.explosion_element[player_nr];
5527 artwork_element = explosion_element;
5532 if (mode == EX_TYPE_NORMAL ||
5533 mode == EX_TYPE_CENTER ||
5534 mode == EX_TYPE_CROSS)
5535 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5538 last_phase = element_info[explosion_element].explosion_delay + 1;
5540 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5542 int xx = x - ex + 1;
5543 int yy = y - ey + 1;
5546 if (!IN_LEV_FIELD(x, y) ||
5547 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5548 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5551 element = Feld[x][y];
5553 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5555 element = MovingOrBlocked2Element(x, y);
5557 if (!IS_EXPLOSION_PROOF(element))
5558 RemoveMovingField(x, y);
5561 /* indestructible elements can only explode in center (but not flames) */
5562 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5563 mode == EX_TYPE_BORDER)) ||
5564 element == EL_FLAMES)
5567 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5568 behaviour, for example when touching a yamyam that explodes to rocks
5569 with active deadly shield, a rock is created under the player !!! */
5570 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5572 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5573 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5574 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5576 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5579 if (IS_ACTIVE_BOMB(element))
5581 /* re-activate things under the bomb like gate or penguin */
5582 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5589 /* save walkable background elements while explosion on same tile */
5590 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5591 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5592 Back[x][y] = element;
5594 /* ignite explodable elements reached by other explosion */
5595 if (element == EL_EXPLOSION)
5596 element = Store2[x][y];
5598 if (AmoebaNr[x][y] &&
5599 (element == EL_AMOEBA_FULL ||
5600 element == EL_BD_AMOEBA ||
5601 element == EL_AMOEBA_GROWING))
5603 AmoebaCnt[AmoebaNr[x][y]]--;
5604 AmoebaCnt2[AmoebaNr[x][y]]--;
5609 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5611 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5613 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5615 if (PLAYERINFO(ex, ey)->use_murphy)
5616 Store[x][y] = EL_EMPTY;
5619 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5620 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5621 else if (ELEM_IS_PLAYER(center_element))
5622 Store[x][y] = EL_EMPTY;
5623 else if (center_element == EL_YAMYAM)
5624 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5625 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5626 Store[x][y] = element_info[center_element].content.e[xx][yy];
5628 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5629 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5630 otherwise) -- FIX THIS !!! */
5631 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5632 Store[x][y] = element_info[element].content.e[1][1];
5634 else if (!CAN_EXPLODE(element))
5635 Store[x][y] = element_info[element].content.e[1][1];
5638 Store[x][y] = EL_EMPTY;
5640 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5641 center_element == EL_AMOEBA_TO_DIAMOND)
5642 Store2[x][y] = element;
5644 Feld[x][y] = EL_EXPLOSION;
5645 GfxElement[x][y] = artwork_element;
5647 ExplodePhase[x][y] = 1;
5648 ExplodeDelay[x][y] = last_phase;
5653 if (center_element == EL_YAMYAM)
5654 game.yamyam_content_nr =
5655 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5667 GfxFrame[x][y] = 0; /* restart explosion animation */
5669 last_phase = ExplodeDelay[x][y];
5671 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5675 /* activate this even in non-DEBUG version until cause for crash in
5676 getGraphicAnimationFrame() (see below) is found and eliminated */
5682 /* this can happen if the player leaves an explosion just in time */
5683 if (GfxElement[x][y] == EL_UNDEFINED)
5684 GfxElement[x][y] = EL_EMPTY;
5686 if (GfxElement[x][y] == EL_UNDEFINED)
5689 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5690 printf("Explode(): This should never happen!\n");
5693 GfxElement[x][y] = EL_EMPTY;
5699 border_element = Store2[x][y];
5700 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5701 border_element = StorePlayer[x][y];
5703 if (phase == element_info[border_element].ignition_delay ||
5704 phase == last_phase)
5706 boolean border_explosion = FALSE;
5708 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5709 !PLAYER_EXPLOSION_PROTECTED(x, y))
5711 KillPlayerUnlessExplosionProtected(x, y);
5712 border_explosion = TRUE;
5714 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5716 Feld[x][y] = Store2[x][y];
5719 border_explosion = TRUE;
5721 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5723 AmoebeUmwandeln(x, y);
5725 border_explosion = TRUE;
5728 /* if an element just explodes due to another explosion (chain-reaction),
5729 do not immediately end the new explosion when it was the last frame of
5730 the explosion (as it would be done in the following "if"-statement!) */
5731 if (border_explosion && phase == last_phase)
5735 if (phase == last_phase)
5739 element = Feld[x][y] = Store[x][y];
5740 Store[x][y] = Store2[x][y] = 0;
5741 GfxElement[x][y] = EL_UNDEFINED;
5743 /* player can escape from explosions and might therefore be still alive */
5744 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5745 element <= EL_PLAYER_IS_EXPLODING_4)
5747 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5748 int explosion_element = EL_PLAYER_1 + player_nr;
5749 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5750 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5752 if (level.use_explosion_element[player_nr])
5753 explosion_element = level.explosion_element[player_nr];
5755 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5756 element_info[explosion_element].content.e[xx][yy]);
5759 /* restore probably existing indestructible background element */
5760 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5761 element = Feld[x][y] = Back[x][y];
5764 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5765 GfxDir[x][y] = MV_NONE;
5766 ChangeDelay[x][y] = 0;
5767 ChangePage[x][y] = -1;
5769 #if USE_NEW_CUSTOM_VALUE
5770 CustomValue[x][y] = 0;
5773 InitField_WithBug2(x, y, FALSE);
5775 DrawLevelField(x, y);
5777 TestIfElementTouchesCustomElement(x, y);
5779 if (GFX_CRUMBLED(element))
5780 DrawLevelFieldCrumbledSandNeighbours(x, y);
5782 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5783 StorePlayer[x][y] = 0;
5785 if (ELEM_IS_PLAYER(element))
5786 RelocatePlayer(x, y, element);
5788 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5790 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5791 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5794 DrawLevelFieldCrumbledSand(x, y);
5796 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5798 DrawLevelElement(x, y, Back[x][y]);
5799 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5801 else if (IS_WALKABLE_UNDER(Back[x][y]))
5803 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5804 DrawLevelElementThruMask(x, y, Back[x][y]);
5806 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5807 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5811 void DynaExplode(int ex, int ey)
5814 int dynabomb_element = Feld[ex][ey];
5815 int dynabomb_size = 1;
5816 boolean dynabomb_xl = FALSE;
5817 struct PlayerInfo *player;
5818 static int xy[4][2] =
5826 if (IS_ACTIVE_BOMB(dynabomb_element))
5828 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5829 dynabomb_size = player->dynabomb_size;
5830 dynabomb_xl = player->dynabomb_xl;
5831 player->dynabombs_left++;
5834 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5836 for (i = 0; i < NUM_DIRECTIONS; i++)
5838 for (j = 1; j <= dynabomb_size; j++)
5840 int x = ex + j * xy[i][0];
5841 int y = ey + j * xy[i][1];
5844 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5847 element = Feld[x][y];
5849 /* do not restart explosions of fields with active bombs */
5850 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5853 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5855 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5856 !IS_DIGGABLE(element) && !dynabomb_xl)
5862 void Bang(int x, int y)
5864 int element = MovingOrBlocked2Element(x, y);
5865 int explosion_type = EX_TYPE_NORMAL;
5867 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5869 struct PlayerInfo *player = PLAYERINFO(x, y);
5871 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
5872 player->element_nr);
5874 if (level.use_explosion_element[player->index_nr])
5876 int explosion_element = level.explosion_element[player->index_nr];
5878 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5879 explosion_type = EX_TYPE_CROSS;
5880 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5881 explosion_type = EX_TYPE_CENTER;
5889 case EL_BD_BUTTERFLY:
5892 case EL_DARK_YAMYAM:
5896 RaiseScoreElement(element);
5899 case EL_DYNABOMB_PLAYER_1_ACTIVE:
5900 case EL_DYNABOMB_PLAYER_2_ACTIVE:
5901 case EL_DYNABOMB_PLAYER_3_ACTIVE:
5902 case EL_DYNABOMB_PLAYER_4_ACTIVE:
5903 case EL_DYNABOMB_INCREASE_NUMBER:
5904 case EL_DYNABOMB_INCREASE_SIZE:
5905 case EL_DYNABOMB_INCREASE_POWER:
5906 explosion_type = EX_TYPE_DYNA;
5909 case EL_DC_LANDMINE:
5911 case EL_EM_EXIT_OPEN:
5912 case EL_EM_STEEL_EXIT_OPEN:
5914 explosion_type = EX_TYPE_CENTER;
5919 case EL_LAMP_ACTIVE:
5920 case EL_AMOEBA_TO_DIAMOND:
5921 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
5922 explosion_type = EX_TYPE_CENTER;
5926 if (element_info[element].explosion_type == EXPLODES_CROSS)
5927 explosion_type = EX_TYPE_CROSS;
5928 else if (element_info[element].explosion_type == EXPLODES_1X1)
5929 explosion_type = EX_TYPE_CENTER;
5933 if (explosion_type == EX_TYPE_DYNA)
5936 Explode(x, y, EX_PHASE_START, explosion_type);
5938 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5941 void SplashAcid(int x, int y)
5943 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5944 (!IN_LEV_FIELD(x - 1, y - 2) ||
5945 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5946 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5948 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5949 (!IN_LEV_FIELD(x + 1, y - 2) ||
5950 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5951 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5953 PlayLevelSound(x, y, SND_ACID_SPLASHING);
5956 static void InitBeltMovement()
5958 static int belt_base_element[4] =
5960 EL_CONVEYOR_BELT_1_LEFT,
5961 EL_CONVEYOR_BELT_2_LEFT,
5962 EL_CONVEYOR_BELT_3_LEFT,
5963 EL_CONVEYOR_BELT_4_LEFT
5965 static int belt_base_active_element[4] =
5967 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5968 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5969 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5970 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5975 /* set frame order for belt animation graphic according to belt direction */
5976 for (i = 0; i < NUM_BELTS; i++)
5980 for (j = 0; j < NUM_BELT_PARTS; j++)
5982 int element = belt_base_active_element[belt_nr] + j;
5983 int graphic_1 = el2img(element);
5984 int graphic_2 = el2panelimg(element);
5986 if (game.belt_dir[i] == MV_LEFT)
5988 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5989 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5993 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5994 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5999 SCAN_PLAYFIELD(x, y)
6001 int element = Feld[x][y];
6003 for (i = 0; i < NUM_BELTS; i++)
6005 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6007 int e_belt_nr = getBeltNrFromBeltElement(element);
6010 if (e_belt_nr == belt_nr)
6012 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6014 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6021 static void ToggleBeltSwitch(int x, int y)
6023 static int belt_base_element[4] =
6025 EL_CONVEYOR_BELT_1_LEFT,
6026 EL_CONVEYOR_BELT_2_LEFT,
6027 EL_CONVEYOR_BELT_3_LEFT,
6028 EL_CONVEYOR_BELT_4_LEFT
6030 static int belt_base_active_element[4] =
6032 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6033 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6034 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6035 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6037 static int belt_base_switch_element[4] =
6039 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6040 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6041 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6042 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6044 static int belt_move_dir[4] =
6052 int element = Feld[x][y];
6053 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6054 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6055 int belt_dir = belt_move_dir[belt_dir_nr];
6058 if (!IS_BELT_SWITCH(element))
6061 game.belt_dir_nr[belt_nr] = belt_dir_nr;
6062 game.belt_dir[belt_nr] = belt_dir;
6064 if (belt_dir_nr == 3)
6067 /* set frame order for belt animation graphic according to belt direction */
6068 for (i = 0; i < NUM_BELT_PARTS; i++)
6070 int element = belt_base_active_element[belt_nr] + i;
6071 int graphic_1 = el2img(element);
6072 int graphic_2 = el2panelimg(element);
6074 if (belt_dir == MV_LEFT)
6076 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6077 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6081 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6082 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6086 SCAN_PLAYFIELD(xx, yy)
6088 int element = Feld[xx][yy];
6090 if (IS_BELT_SWITCH(element))
6092 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6094 if (e_belt_nr == belt_nr)
6096 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6097 DrawLevelField(xx, yy);
6100 else if (IS_BELT(element) && belt_dir != MV_NONE)
6102 int e_belt_nr = getBeltNrFromBeltElement(element);
6104 if (e_belt_nr == belt_nr)
6106 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6108 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6109 DrawLevelField(xx, yy);
6112 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6114 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6116 if (e_belt_nr == belt_nr)
6118 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6120 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6121 DrawLevelField(xx, yy);
6127 static void ToggleSwitchgateSwitch(int x, int y)
6131 game.switchgate_pos = !game.switchgate_pos;
6133 SCAN_PLAYFIELD(xx, yy)
6135 int element = Feld[xx][yy];
6137 #if !USE_BOTH_SWITCHGATE_SWITCHES
6138 if (element == EL_SWITCHGATE_SWITCH_UP ||
6139 element == EL_SWITCHGATE_SWITCH_DOWN)
6141 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6142 DrawLevelField(xx, yy);
6144 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6145 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6147 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6148 DrawLevelField(xx, yy);
6151 if (element == EL_SWITCHGATE_SWITCH_UP)
6153 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6154 DrawLevelField(xx, yy);
6156 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6158 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6159 DrawLevelField(xx, yy);
6161 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6163 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6164 DrawLevelField(xx, yy);
6166 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6168 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6169 DrawLevelField(xx, yy);
6172 else if (element == EL_SWITCHGATE_OPEN ||
6173 element == EL_SWITCHGATE_OPENING)
6175 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6177 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6179 else if (element == EL_SWITCHGATE_CLOSED ||
6180 element == EL_SWITCHGATE_CLOSING)
6182 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6184 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6189 static int getInvisibleActiveFromInvisibleElement(int element)
6191 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6192 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6193 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6197 static int getInvisibleFromInvisibleActiveElement(int element)
6199 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6200 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6201 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6205 static void RedrawAllLightSwitchesAndInvisibleElements()
6209 SCAN_PLAYFIELD(x, y)
6211 int element = Feld[x][y];
6213 if (element == EL_LIGHT_SWITCH &&
6214 game.light_time_left > 0)
6216 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6217 DrawLevelField(x, y);
6219 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6220 game.light_time_left == 0)
6222 Feld[x][y] = EL_LIGHT_SWITCH;
6223 DrawLevelField(x, y);
6225 else if (element == EL_EMC_DRIPPER &&
6226 game.light_time_left > 0)
6228 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6229 DrawLevelField(x, y);
6231 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6232 game.light_time_left == 0)
6234 Feld[x][y] = EL_EMC_DRIPPER;
6235 DrawLevelField(x, y);
6237 else if (element == EL_INVISIBLE_STEELWALL ||
6238 element == EL_INVISIBLE_WALL ||
6239 element == EL_INVISIBLE_SAND)
6241 if (game.light_time_left > 0)
6242 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6244 DrawLevelField(x, y);
6246 /* uncrumble neighbour fields, if needed */
6247 if (element == EL_INVISIBLE_SAND)
6248 DrawLevelFieldCrumbledSandNeighbours(x, y);
6250 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6251 element == EL_INVISIBLE_WALL_ACTIVE ||
6252 element == EL_INVISIBLE_SAND_ACTIVE)
6254 if (game.light_time_left == 0)
6255 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6257 DrawLevelField(x, y);
6259 /* re-crumble neighbour fields, if needed */
6260 if (element == EL_INVISIBLE_SAND)
6261 DrawLevelFieldCrumbledSandNeighbours(x, y);
6266 static void RedrawAllInvisibleElementsForLenses()
6270 SCAN_PLAYFIELD(x, y)
6272 int element = Feld[x][y];
6274 if (element == EL_EMC_DRIPPER &&
6275 game.lenses_time_left > 0)
6277 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6278 DrawLevelField(x, y);
6280 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6281 game.lenses_time_left == 0)
6283 Feld[x][y] = EL_EMC_DRIPPER;
6284 DrawLevelField(x, y);
6286 else if (element == EL_INVISIBLE_STEELWALL ||
6287 element == EL_INVISIBLE_WALL ||
6288 element == EL_INVISIBLE_SAND)
6290 if (game.lenses_time_left > 0)
6291 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6293 DrawLevelField(x, y);
6295 /* uncrumble neighbour fields, if needed */
6296 if (element == EL_INVISIBLE_SAND)
6297 DrawLevelFieldCrumbledSandNeighbours(x, y);
6299 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6300 element == EL_INVISIBLE_WALL_ACTIVE ||
6301 element == EL_INVISIBLE_SAND_ACTIVE)
6303 if (game.lenses_time_left == 0)
6304 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6306 DrawLevelField(x, y);
6308 /* re-crumble neighbour fields, if needed */
6309 if (element == EL_INVISIBLE_SAND)
6310 DrawLevelFieldCrumbledSandNeighbours(x, y);
6315 static void RedrawAllInvisibleElementsForMagnifier()
6319 SCAN_PLAYFIELD(x, y)
6321 int element = Feld[x][y];
6323 if (element == EL_EMC_FAKE_GRASS &&
6324 game.magnify_time_left > 0)
6326 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6327 DrawLevelField(x, y);
6329 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6330 game.magnify_time_left == 0)
6332 Feld[x][y] = EL_EMC_FAKE_GRASS;
6333 DrawLevelField(x, y);
6335 else if (IS_GATE_GRAY(element) &&
6336 game.magnify_time_left > 0)
6338 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6339 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6340 IS_EM_GATE_GRAY(element) ?
6341 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6342 IS_EMC_GATE_GRAY(element) ?
6343 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6345 DrawLevelField(x, y);
6347 else if (IS_GATE_GRAY_ACTIVE(element) &&
6348 game.magnify_time_left == 0)
6350 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6351 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6352 IS_EM_GATE_GRAY_ACTIVE(element) ?
6353 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6354 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6355 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6357 DrawLevelField(x, y);
6362 static void ToggleLightSwitch(int x, int y)
6364 int element = Feld[x][y];
6366 game.light_time_left =
6367 (element == EL_LIGHT_SWITCH ?
6368 level.time_light * FRAMES_PER_SECOND : 0);
6370 RedrawAllLightSwitchesAndInvisibleElements();
6373 static void ActivateTimegateSwitch(int x, int y)
6377 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6379 SCAN_PLAYFIELD(xx, yy)
6381 int element = Feld[xx][yy];
6383 if (element == EL_TIMEGATE_CLOSED ||
6384 element == EL_TIMEGATE_CLOSING)
6386 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6387 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6391 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6393 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6394 DrawLevelField(xx, yy);
6401 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6402 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6404 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6408 void Impact(int x, int y)
6410 boolean last_line = (y == lev_fieldy - 1);
6411 boolean object_hit = FALSE;
6412 boolean impact = (last_line || object_hit);
6413 int element = Feld[x][y];
6414 int smashed = EL_STEELWALL;
6416 if (!last_line) /* check if element below was hit */
6418 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6421 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6422 MovDir[x][y + 1] != MV_DOWN ||
6423 MovPos[x][y + 1] <= TILEY / 2));
6425 /* do not smash moving elements that left the smashed field in time */
6426 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6427 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6430 #if USE_QUICKSAND_IMPACT_BUGFIX
6431 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6433 RemoveMovingField(x, y + 1);
6434 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6435 Feld[x][y + 2] = EL_ROCK;
6436 DrawLevelField(x, y + 2);
6441 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6443 RemoveMovingField(x, y + 1);
6444 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6445 Feld[x][y + 2] = EL_ROCK;
6446 DrawLevelField(x, y + 2);
6453 smashed = MovingOrBlocked2Element(x, y + 1);
6455 impact = (last_line || object_hit);
6458 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6460 SplashAcid(x, y + 1);
6464 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6465 /* only reset graphic animation if graphic really changes after impact */
6467 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6469 ResetGfxAnimation(x, y);
6470 DrawLevelField(x, y);
6473 if (impact && CAN_EXPLODE_IMPACT(element))
6478 else if (impact && element == EL_PEARL &&
6479 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6481 ResetGfxAnimation(x, y);
6483 Feld[x][y] = EL_PEARL_BREAKING;
6484 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6487 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6489 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6494 if (impact && element == EL_AMOEBA_DROP)
6496 if (object_hit && IS_PLAYER(x, y + 1))
6497 KillPlayerUnlessEnemyProtected(x, y + 1);
6498 else if (object_hit && smashed == EL_PENGUIN)
6502 Feld[x][y] = EL_AMOEBA_GROWING;
6503 Store[x][y] = EL_AMOEBA_WET;
6505 ResetRandomAnimationValue(x, y);
6510 if (object_hit) /* check which object was hit */
6512 if ((CAN_PASS_MAGIC_WALL(element) &&
6513 (smashed == EL_MAGIC_WALL ||
6514 smashed == EL_BD_MAGIC_WALL)) ||
6515 (CAN_PASS_DC_MAGIC_WALL(element) &&
6516 smashed == EL_DC_MAGIC_WALL))
6519 int activated_magic_wall =
6520 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6521 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6522 EL_DC_MAGIC_WALL_ACTIVE);
6524 /* activate magic wall / mill */
6525 SCAN_PLAYFIELD(xx, yy)
6527 if (Feld[xx][yy] == smashed)
6528 Feld[xx][yy] = activated_magic_wall;
6531 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6532 game.magic_wall_active = TRUE;
6534 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6535 SND_MAGIC_WALL_ACTIVATING :
6536 smashed == EL_BD_MAGIC_WALL ?
6537 SND_BD_MAGIC_WALL_ACTIVATING :
6538 SND_DC_MAGIC_WALL_ACTIVATING));
6541 if (IS_PLAYER(x, y + 1))
6543 if (CAN_SMASH_PLAYER(element))
6545 KillPlayerUnlessEnemyProtected(x, y + 1);
6549 else if (smashed == EL_PENGUIN)
6551 if (CAN_SMASH_PLAYER(element))
6557 else if (element == EL_BD_DIAMOND)
6559 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6565 else if (((element == EL_SP_INFOTRON ||
6566 element == EL_SP_ZONK) &&
6567 (smashed == EL_SP_SNIKSNAK ||
6568 smashed == EL_SP_ELECTRON ||
6569 smashed == EL_SP_DISK_ORANGE)) ||
6570 (element == EL_SP_INFOTRON &&
6571 smashed == EL_SP_DISK_YELLOW))
6576 else if (CAN_SMASH_EVERYTHING(element))
6578 if (IS_CLASSIC_ENEMY(smashed) ||
6579 CAN_EXPLODE_SMASHED(smashed))
6584 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6586 if (smashed == EL_LAMP ||
6587 smashed == EL_LAMP_ACTIVE)
6592 else if (smashed == EL_NUT)
6594 Feld[x][y + 1] = EL_NUT_BREAKING;
6595 PlayLevelSound(x, y, SND_NUT_BREAKING);
6596 RaiseScoreElement(EL_NUT);
6599 else if (smashed == EL_PEARL)
6601 ResetGfxAnimation(x, y);
6603 Feld[x][y + 1] = EL_PEARL_BREAKING;
6604 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6607 else if (smashed == EL_DIAMOND)
6609 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6610 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6613 else if (IS_BELT_SWITCH(smashed))
6615 ToggleBeltSwitch(x, y + 1);
6617 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6618 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6619 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6620 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6622 ToggleSwitchgateSwitch(x, y + 1);
6624 else if (smashed == EL_LIGHT_SWITCH ||
6625 smashed == EL_LIGHT_SWITCH_ACTIVE)
6627 ToggleLightSwitch(x, y + 1);
6632 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6635 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6637 CheckElementChangeBySide(x, y + 1, smashed, element,
6638 CE_SWITCHED, CH_SIDE_TOP);
6639 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6645 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6650 /* play sound of magic wall / mill */
6652 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6653 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6654 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6656 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6657 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6658 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6659 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6660 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6661 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6666 /* play sound of object that hits the ground */
6667 if (last_line || object_hit)
6668 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6671 inline static void TurnRoundExt(int x, int y)
6683 { 0, 0 }, { 0, 0 }, { 0, 0 },
6688 int left, right, back;
6692 { MV_DOWN, MV_UP, MV_RIGHT },
6693 { MV_UP, MV_DOWN, MV_LEFT },
6695 { MV_LEFT, MV_RIGHT, MV_DOWN },
6699 { MV_RIGHT, MV_LEFT, MV_UP }
6702 int element = Feld[x][y];
6703 int move_pattern = element_info[element].move_pattern;
6705 int old_move_dir = MovDir[x][y];
6706 int left_dir = turn[old_move_dir].left;
6707 int right_dir = turn[old_move_dir].right;
6708 int back_dir = turn[old_move_dir].back;
6710 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6711 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6712 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6713 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6715 int left_x = x + left_dx, left_y = y + left_dy;
6716 int right_x = x + right_dx, right_y = y + right_dy;
6717 int move_x = x + move_dx, move_y = y + move_dy;
6721 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6723 TestIfBadThingTouchesOtherBadThing(x, y);
6725 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6726 MovDir[x][y] = right_dir;
6727 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6728 MovDir[x][y] = left_dir;
6730 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6732 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6735 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6737 TestIfBadThingTouchesOtherBadThing(x, y);
6739 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6740 MovDir[x][y] = left_dir;
6741 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6742 MovDir[x][y] = right_dir;
6744 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6746 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6749 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6751 TestIfBadThingTouchesOtherBadThing(x, y);
6753 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6754 MovDir[x][y] = left_dir;
6755 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6756 MovDir[x][y] = right_dir;
6758 if (MovDir[x][y] != old_move_dir)
6761 else if (element == EL_YAMYAM)
6763 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6764 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6766 if (can_turn_left && can_turn_right)
6767 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6768 else if (can_turn_left)
6769 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6770 else if (can_turn_right)
6771 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6773 MovDir[x][y] = back_dir;
6775 MovDelay[x][y] = 16 + 16 * RND(3);
6777 else if (element == EL_DARK_YAMYAM)
6779 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6781 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6784 if (can_turn_left && can_turn_right)
6785 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6786 else if (can_turn_left)
6787 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6788 else if (can_turn_right)
6789 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6791 MovDir[x][y] = back_dir;
6793 MovDelay[x][y] = 16 + 16 * RND(3);
6795 else if (element == EL_PACMAN)
6797 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6798 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6800 if (can_turn_left && can_turn_right)
6801 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6802 else if (can_turn_left)
6803 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6804 else if (can_turn_right)
6805 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6807 MovDir[x][y] = back_dir;
6809 MovDelay[x][y] = 6 + RND(40);
6811 else if (element == EL_PIG)
6813 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6814 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6815 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6816 boolean should_turn_left, should_turn_right, should_move_on;
6818 int rnd = RND(rnd_value);
6820 should_turn_left = (can_turn_left &&
6822 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6823 y + back_dy + left_dy)));
6824 should_turn_right = (can_turn_right &&
6826 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6827 y + back_dy + right_dy)));
6828 should_move_on = (can_move_on &&
6831 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6832 y + move_dy + left_dy) ||
6833 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6834 y + move_dy + right_dy)));
6836 if (should_turn_left || should_turn_right || should_move_on)
6838 if (should_turn_left && should_turn_right && should_move_on)
6839 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6840 rnd < 2 * rnd_value / 3 ? right_dir :
6842 else if (should_turn_left && should_turn_right)
6843 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6844 else if (should_turn_left && should_move_on)
6845 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6846 else if (should_turn_right && should_move_on)
6847 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6848 else if (should_turn_left)
6849 MovDir[x][y] = left_dir;
6850 else if (should_turn_right)
6851 MovDir[x][y] = right_dir;
6852 else if (should_move_on)
6853 MovDir[x][y] = old_move_dir;
6855 else if (can_move_on && rnd > rnd_value / 8)
6856 MovDir[x][y] = old_move_dir;
6857 else if (can_turn_left && can_turn_right)
6858 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6859 else if (can_turn_left && rnd > rnd_value / 8)
6860 MovDir[x][y] = left_dir;
6861 else if (can_turn_right && rnd > rnd_value/8)
6862 MovDir[x][y] = right_dir;
6864 MovDir[x][y] = back_dir;
6866 xx = x + move_xy[MovDir[x][y]].dx;
6867 yy = y + move_xy[MovDir[x][y]].dy;
6869 if (!IN_LEV_FIELD(xx, yy) ||
6870 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6871 MovDir[x][y] = old_move_dir;
6875 else if (element == EL_DRAGON)
6877 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6878 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6879 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6881 int rnd = RND(rnd_value);
6883 if (can_move_on && rnd > rnd_value / 8)
6884 MovDir[x][y] = old_move_dir;
6885 else if (can_turn_left && can_turn_right)
6886 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6887 else if (can_turn_left && rnd > rnd_value / 8)
6888 MovDir[x][y] = left_dir;
6889 else if (can_turn_right && rnd > rnd_value / 8)
6890 MovDir[x][y] = right_dir;
6892 MovDir[x][y] = back_dir;
6894 xx = x + move_xy[MovDir[x][y]].dx;
6895 yy = y + move_xy[MovDir[x][y]].dy;
6897 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6898 MovDir[x][y] = old_move_dir;
6902 else if (element == EL_MOLE)
6904 boolean can_move_on =
6905 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6906 IS_AMOEBOID(Feld[move_x][move_y]) ||
6907 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6910 boolean can_turn_left =
6911 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6912 IS_AMOEBOID(Feld[left_x][left_y])));
6914 boolean can_turn_right =
6915 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6916 IS_AMOEBOID(Feld[right_x][right_y])));
6918 if (can_turn_left && can_turn_right)
6919 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6920 else if (can_turn_left)
6921 MovDir[x][y] = left_dir;
6923 MovDir[x][y] = right_dir;
6926 if (MovDir[x][y] != old_move_dir)
6929 else if (element == EL_BALLOON)
6931 MovDir[x][y] = game.wind_direction;
6934 else if (element == EL_SPRING)
6936 #if USE_NEW_SPRING_BUMPER
6937 if (MovDir[x][y] & MV_HORIZONTAL)
6939 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6940 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6942 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6943 ResetGfxAnimation(move_x, move_y);
6944 DrawLevelField(move_x, move_y);
6946 MovDir[x][y] = back_dir;
6948 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6949 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6950 MovDir[x][y] = MV_NONE;
6953 if (MovDir[x][y] & MV_HORIZONTAL &&
6954 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6955 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
6956 MovDir[x][y] = MV_NONE;
6961 else if (element == EL_ROBOT ||
6962 element == EL_SATELLITE ||
6963 element == EL_PENGUIN ||
6964 element == EL_EMC_ANDROID)
6966 int attr_x = -1, attr_y = -1;
6977 for (i = 0; i < MAX_PLAYERS; i++)
6979 struct PlayerInfo *player = &stored_player[i];
6980 int jx = player->jx, jy = player->jy;
6982 if (!player->active)
6986 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6994 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6995 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6996 game.engine_version < VERSION_IDENT(3,1,0,0)))
7002 if (element == EL_PENGUIN)
7005 static int xy[4][2] =
7013 for (i = 0; i < NUM_DIRECTIONS; i++)
7015 int ex = x + xy[i][0];
7016 int ey = y + xy[i][1];
7018 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7019 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7020 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7021 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7030 MovDir[x][y] = MV_NONE;
7032 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7033 else if (attr_x > x)
7034 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7036 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7037 else if (attr_y > y)
7038 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7040 if (element == EL_ROBOT)
7044 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7045 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7046 Moving2Blocked(x, y, &newx, &newy);
7048 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7049 MovDelay[x][y] = 8 + 8 * !RND(3);
7051 MovDelay[x][y] = 16;
7053 else if (element == EL_PENGUIN)
7059 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7061 boolean first_horiz = RND(2);
7062 int new_move_dir = MovDir[x][y];
7065 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7066 Moving2Blocked(x, y, &newx, &newy);
7068 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7072 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7073 Moving2Blocked(x, y, &newx, &newy);
7075 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7078 MovDir[x][y] = old_move_dir;
7082 else if (element == EL_SATELLITE)
7088 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7090 boolean first_horiz = RND(2);
7091 int new_move_dir = MovDir[x][y];
7094 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7095 Moving2Blocked(x, y, &newx, &newy);
7097 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7101 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7102 Moving2Blocked(x, y, &newx, &newy);
7104 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7107 MovDir[x][y] = old_move_dir;
7111 else if (element == EL_EMC_ANDROID)
7113 static int check_pos[16] =
7115 -1, /* 0 => (invalid) */
7116 7, /* 1 => MV_LEFT */
7117 3, /* 2 => MV_RIGHT */
7118 -1, /* 3 => (invalid) */
7120 0, /* 5 => MV_LEFT | MV_UP */
7121 2, /* 6 => MV_RIGHT | MV_UP */
7122 -1, /* 7 => (invalid) */
7123 5, /* 8 => MV_DOWN */
7124 6, /* 9 => MV_LEFT | MV_DOWN */
7125 4, /* 10 => MV_RIGHT | MV_DOWN */
7126 -1, /* 11 => (invalid) */
7127 -1, /* 12 => (invalid) */
7128 -1, /* 13 => (invalid) */
7129 -1, /* 14 => (invalid) */
7130 -1, /* 15 => (invalid) */
7138 { -1, -1, MV_LEFT | MV_UP },
7140 { +1, -1, MV_RIGHT | MV_UP },
7141 { +1, 0, MV_RIGHT },
7142 { +1, +1, MV_RIGHT | MV_DOWN },
7144 { -1, +1, MV_LEFT | MV_DOWN },
7147 int start_pos, check_order;
7148 boolean can_clone = FALSE;
7151 /* check if there is any free field around current position */
7152 for (i = 0; i < 8; i++)
7154 int newx = x + check_xy[i].dx;
7155 int newy = y + check_xy[i].dy;
7157 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7165 if (can_clone) /* randomly find an element to clone */
7169 start_pos = check_pos[RND(8)];
7170 check_order = (RND(2) ? -1 : +1);
7172 for (i = 0; i < 8; i++)
7174 int pos_raw = start_pos + i * check_order;
7175 int pos = (pos_raw + 8) % 8;
7176 int newx = x + check_xy[pos].dx;
7177 int newy = y + check_xy[pos].dy;
7179 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7181 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7182 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7184 Store[x][y] = Feld[newx][newy];
7193 if (can_clone) /* randomly find a direction to move */
7197 start_pos = check_pos[RND(8)];
7198 check_order = (RND(2) ? -1 : +1);
7200 for (i = 0; i < 8; i++)
7202 int pos_raw = start_pos + i * check_order;
7203 int pos = (pos_raw + 8) % 8;
7204 int newx = x + check_xy[pos].dx;
7205 int newy = y + check_xy[pos].dy;
7206 int new_move_dir = check_xy[pos].dir;
7208 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7210 MovDir[x][y] = new_move_dir;
7211 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7220 if (can_clone) /* cloning and moving successful */
7223 /* cannot clone -- try to move towards player */
7225 start_pos = check_pos[MovDir[x][y] & 0x0f];
7226 check_order = (RND(2) ? -1 : +1);
7228 for (i = 0; i < 3; i++)
7230 /* first check start_pos, then previous/next or (next/previous) pos */
7231 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7232 int pos = (pos_raw + 8) % 8;
7233 int newx = x + check_xy[pos].dx;
7234 int newy = y + check_xy[pos].dy;
7235 int new_move_dir = check_xy[pos].dir;
7237 if (IS_PLAYER(newx, newy))
7240 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7242 MovDir[x][y] = new_move_dir;
7243 MovDelay[x][y] = level.android_move_time * 8 + 1;
7250 else if (move_pattern == MV_TURNING_LEFT ||
7251 move_pattern == MV_TURNING_RIGHT ||
7252 move_pattern == MV_TURNING_LEFT_RIGHT ||
7253 move_pattern == MV_TURNING_RIGHT_LEFT ||
7254 move_pattern == MV_TURNING_RANDOM ||
7255 move_pattern == MV_ALL_DIRECTIONS)
7257 boolean can_turn_left =
7258 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7259 boolean can_turn_right =
7260 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7262 if (element_info[element].move_stepsize == 0) /* "not moving" */
7265 if (move_pattern == MV_TURNING_LEFT)
7266 MovDir[x][y] = left_dir;
7267 else if (move_pattern == MV_TURNING_RIGHT)
7268 MovDir[x][y] = right_dir;
7269 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7270 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7271 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7272 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7273 else if (move_pattern == MV_TURNING_RANDOM)
7274 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7275 can_turn_right && !can_turn_left ? right_dir :
7276 RND(2) ? left_dir : right_dir);
7277 else if (can_turn_left && can_turn_right)
7278 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7279 else if (can_turn_left)
7280 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7281 else if (can_turn_right)
7282 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7284 MovDir[x][y] = back_dir;
7286 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7288 else if (move_pattern == MV_HORIZONTAL ||
7289 move_pattern == MV_VERTICAL)
7291 if (move_pattern & old_move_dir)
7292 MovDir[x][y] = back_dir;
7293 else if (move_pattern == MV_HORIZONTAL)
7294 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7295 else if (move_pattern == MV_VERTICAL)
7296 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7298 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7300 else if (move_pattern & MV_ANY_DIRECTION)
7302 MovDir[x][y] = move_pattern;
7303 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7305 else if (move_pattern & MV_WIND_DIRECTION)
7307 MovDir[x][y] = game.wind_direction;
7308 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7310 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7312 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7313 MovDir[x][y] = left_dir;
7314 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7315 MovDir[x][y] = right_dir;
7317 if (MovDir[x][y] != old_move_dir)
7318 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7320 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7322 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7323 MovDir[x][y] = right_dir;
7324 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7325 MovDir[x][y] = left_dir;
7327 if (MovDir[x][y] != old_move_dir)
7328 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7330 else if (move_pattern == MV_TOWARDS_PLAYER ||
7331 move_pattern == MV_AWAY_FROM_PLAYER)
7333 int attr_x = -1, attr_y = -1;
7335 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7346 for (i = 0; i < MAX_PLAYERS; i++)
7348 struct PlayerInfo *player = &stored_player[i];
7349 int jx = player->jx, jy = player->jy;
7351 if (!player->active)
7355 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7363 MovDir[x][y] = MV_NONE;
7365 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7366 else if (attr_x > x)
7367 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7369 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7370 else if (attr_y > y)
7371 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7373 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7375 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7377 boolean first_horiz = RND(2);
7378 int new_move_dir = MovDir[x][y];
7380 if (element_info[element].move_stepsize == 0) /* "not moving" */
7382 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7383 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7389 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7390 Moving2Blocked(x, y, &newx, &newy);
7392 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7396 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7397 Moving2Blocked(x, y, &newx, &newy);
7399 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7402 MovDir[x][y] = old_move_dir;
7405 else if (move_pattern == MV_WHEN_PUSHED ||
7406 move_pattern == MV_WHEN_DROPPED)
7408 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7409 MovDir[x][y] = MV_NONE;
7413 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7415 static int test_xy[7][2] =
7425 static int test_dir[7] =
7435 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7436 int move_preference = -1000000; /* start with very low preference */
7437 int new_move_dir = MV_NONE;
7438 int start_test = RND(4);
7441 for (i = 0; i < NUM_DIRECTIONS; i++)
7443 int move_dir = test_dir[start_test + i];
7444 int move_dir_preference;
7446 xx = x + test_xy[start_test + i][0];
7447 yy = y + test_xy[start_test + i][1];
7449 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7450 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7452 new_move_dir = move_dir;
7457 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7460 move_dir_preference = -1 * RunnerVisit[xx][yy];
7461 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7462 move_dir_preference = PlayerVisit[xx][yy];
7464 if (move_dir_preference > move_preference)
7466 /* prefer field that has not been visited for the longest time */
7467 move_preference = move_dir_preference;
7468 new_move_dir = move_dir;
7470 else if (move_dir_preference == move_preference &&
7471 move_dir == old_move_dir)
7473 /* prefer last direction when all directions are preferred equally */
7474 move_preference = move_dir_preference;
7475 new_move_dir = move_dir;
7479 MovDir[x][y] = new_move_dir;
7480 if (old_move_dir != new_move_dir)
7481 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7485 static void TurnRound(int x, int y)
7487 int direction = MovDir[x][y];
7491 GfxDir[x][y] = MovDir[x][y];
7493 if (direction != MovDir[x][y])
7497 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7499 ResetGfxFrame(x, y, FALSE);
7502 static boolean JustBeingPushed(int x, int y)
7506 for (i = 0; i < MAX_PLAYERS; i++)
7508 struct PlayerInfo *player = &stored_player[i];
7510 if (player->active && player->is_pushing && player->MovPos)
7512 int next_jx = player->jx + (player->jx - player->last_jx);
7513 int next_jy = player->jy + (player->jy - player->last_jy);
7515 if (x == next_jx && y == next_jy)
7523 void StartMoving(int x, int y)
7525 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7526 int element = Feld[x][y];
7531 if (MovDelay[x][y] == 0)
7532 GfxAction[x][y] = ACTION_DEFAULT;
7534 if (CAN_FALL(element) && y < lev_fieldy - 1)
7536 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7537 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7538 if (JustBeingPushed(x, y))
7541 if (element == EL_QUICKSAND_FULL)
7543 if (IS_FREE(x, y + 1))
7545 InitMovingField(x, y, MV_DOWN);
7546 started_moving = TRUE;
7548 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7549 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7550 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7551 Store[x][y] = EL_ROCK;
7553 Store[x][y] = EL_ROCK;
7556 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7558 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7560 if (!MovDelay[x][y])
7561 MovDelay[x][y] = TILEY + 1;
7570 Feld[x][y] = EL_QUICKSAND_EMPTY;
7571 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7572 Store[x][y + 1] = Store[x][y];
7575 PlayLevelSoundAction(x, y, ACTION_FILLING);
7578 else if (element == EL_QUICKSAND_FAST_FULL)
7580 if (IS_FREE(x, y + 1))
7582 InitMovingField(x, y, MV_DOWN);
7583 started_moving = TRUE;
7585 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7586 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7587 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7588 Store[x][y] = EL_ROCK;
7590 Store[x][y] = EL_ROCK;
7593 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7595 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7597 if (!MovDelay[x][y])
7598 MovDelay[x][y] = TILEY + 1;
7607 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7608 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7609 Store[x][y + 1] = Store[x][y];
7612 PlayLevelSoundAction(x, y, ACTION_FILLING);
7615 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7616 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7618 InitMovingField(x, y, MV_DOWN);
7619 started_moving = TRUE;
7621 Feld[x][y] = EL_QUICKSAND_FILLING;
7622 Store[x][y] = element;
7624 PlayLevelSoundAction(x, y, ACTION_FILLING);
7626 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7627 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7629 InitMovingField(x, y, MV_DOWN);
7630 started_moving = TRUE;
7632 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7633 Store[x][y] = element;
7635 PlayLevelSoundAction(x, y, ACTION_FILLING);
7637 else if (element == EL_MAGIC_WALL_FULL)
7639 if (IS_FREE(x, y + 1))
7641 InitMovingField(x, y, MV_DOWN);
7642 started_moving = TRUE;
7644 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7645 Store[x][y] = EL_CHANGED(Store[x][y]);
7647 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7649 if (!MovDelay[x][y])
7650 MovDelay[x][y] = TILEY/4 + 1;
7659 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7660 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7661 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7665 else if (element == EL_BD_MAGIC_WALL_FULL)
7667 if (IS_FREE(x, y + 1))
7669 InitMovingField(x, y, MV_DOWN);
7670 started_moving = TRUE;
7672 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7673 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7675 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7677 if (!MovDelay[x][y])
7678 MovDelay[x][y] = TILEY/4 + 1;
7687 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7688 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7689 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7693 else if (element == EL_DC_MAGIC_WALL_FULL)
7695 if (IS_FREE(x, y + 1))
7697 InitMovingField(x, y, MV_DOWN);
7698 started_moving = TRUE;
7700 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7701 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7703 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7705 if (!MovDelay[x][y])
7706 MovDelay[x][y] = TILEY/4 + 1;
7715 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7716 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7717 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7721 else if ((CAN_PASS_MAGIC_WALL(element) &&
7722 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7723 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7724 (CAN_PASS_DC_MAGIC_WALL(element) &&
7725 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7728 InitMovingField(x, y, MV_DOWN);
7729 started_moving = TRUE;
7732 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7733 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7734 EL_DC_MAGIC_WALL_FILLING);
7735 Store[x][y] = element;
7737 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7739 SplashAcid(x, y + 1);
7741 InitMovingField(x, y, MV_DOWN);
7742 started_moving = TRUE;
7744 Store[x][y] = EL_ACID;
7747 #if USE_FIX_IMPACT_COLLISION
7748 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7749 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7751 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7752 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7754 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7755 CAN_FALL(element) && WasJustFalling[x][y] &&
7756 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7758 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7759 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7760 (Feld[x][y + 1] == EL_BLOCKED)))
7762 /* this is needed for a special case not covered by calling "Impact()"
7763 from "ContinueMoving()": if an element moves to a tile directly below
7764 another element which was just falling on that tile (which was empty
7765 in the previous frame), the falling element above would just stop
7766 instead of smashing the element below (in previous version, the above
7767 element was just checked for "moving" instead of "falling", resulting
7768 in incorrect smashes caused by horizontal movement of the above
7769 element; also, the case of the player being the element to smash was
7770 simply not covered here... :-/ ) */
7772 CheckCollision[x][y] = 0;
7773 CheckImpact[x][y] = 0;
7777 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7779 if (MovDir[x][y] == MV_NONE)
7781 InitMovingField(x, y, MV_DOWN);
7782 started_moving = TRUE;
7785 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7787 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7788 MovDir[x][y] = MV_DOWN;
7790 InitMovingField(x, y, MV_DOWN);
7791 started_moving = TRUE;
7793 else if (element == EL_AMOEBA_DROP)
7795 Feld[x][y] = EL_AMOEBA_GROWING;
7796 Store[x][y] = EL_AMOEBA_WET;
7798 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7799 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7800 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7801 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7803 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
7804 (IS_FREE(x - 1, y + 1) ||
7805 Feld[x - 1][y + 1] == EL_ACID));
7806 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7807 (IS_FREE(x + 1, y + 1) ||
7808 Feld[x + 1][y + 1] == EL_ACID));
7809 boolean can_fall_any = (can_fall_left || can_fall_right);
7810 boolean can_fall_both = (can_fall_left && can_fall_right);
7811 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7813 #if USE_NEW_ALL_SLIPPERY
7814 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7816 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7817 can_fall_right = FALSE;
7818 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7819 can_fall_left = FALSE;
7820 else if (slippery_type == SLIPPERY_ONLY_LEFT)
7821 can_fall_right = FALSE;
7822 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7823 can_fall_left = FALSE;
7825 can_fall_any = (can_fall_left || can_fall_right);
7826 can_fall_both = FALSE;
7829 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
7831 if (slippery_type == SLIPPERY_ONLY_LEFT)
7832 can_fall_right = FALSE;
7833 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7834 can_fall_left = FALSE;
7835 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7836 can_fall_right = FALSE;
7837 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7838 can_fall_left = FALSE;
7840 can_fall_any = (can_fall_left || can_fall_right);
7841 can_fall_both = (can_fall_left && can_fall_right);
7845 #if USE_NEW_ALL_SLIPPERY
7847 #if USE_NEW_SP_SLIPPERY
7848 /* !!! better use the same properties as for custom elements here !!! */
7849 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
7850 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
7852 can_fall_right = FALSE; /* slip down on left side */
7853 can_fall_both = FALSE;
7858 #if USE_NEW_ALL_SLIPPERY
7861 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7862 can_fall_right = FALSE; /* slip down on left side */
7864 can_fall_left = !(can_fall_right = RND(2));
7866 can_fall_both = FALSE;
7871 if (game.emulation == EMU_BOULDERDASH ||
7872 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7873 can_fall_right = FALSE; /* slip down on left side */
7875 can_fall_left = !(can_fall_right = RND(2));
7877 can_fall_both = FALSE;
7883 /* if not determined otherwise, prefer left side for slipping down */
7884 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7885 started_moving = TRUE;
7889 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
7891 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7894 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
7895 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7896 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7897 int belt_dir = game.belt_dir[belt_nr];
7899 if ((belt_dir == MV_LEFT && left_is_free) ||
7900 (belt_dir == MV_RIGHT && right_is_free))
7902 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7904 InitMovingField(x, y, belt_dir);
7905 started_moving = TRUE;
7907 Pushed[x][y] = TRUE;
7908 Pushed[nextx][y] = TRUE;
7910 GfxAction[x][y] = ACTION_DEFAULT;
7914 MovDir[x][y] = 0; /* if element was moving, stop it */
7919 /* not "else if" because of elements that can fall and move (EL_SPRING) */
7921 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
7923 if (CAN_MOVE(element) && !started_moving)
7926 int move_pattern = element_info[element].move_pattern;
7931 if (MovDir[x][y] == MV_NONE)
7933 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
7934 x, y, element, element_info[element].token_name);
7935 printf("StartMoving(): This should never happen!\n");
7940 Moving2Blocked(x, y, &newx, &newy);
7942 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7945 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7946 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7948 WasJustMoving[x][y] = 0;
7949 CheckCollision[x][y] = 0;
7951 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7953 if (Feld[x][y] != element) /* element has changed */
7957 if (!MovDelay[x][y]) /* start new movement phase */
7959 /* all objects that can change their move direction after each step
7960 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7962 if (element != EL_YAMYAM &&
7963 element != EL_DARK_YAMYAM &&
7964 element != EL_PACMAN &&
7965 !(move_pattern & MV_ANY_DIRECTION) &&
7966 move_pattern != MV_TURNING_LEFT &&
7967 move_pattern != MV_TURNING_RIGHT &&
7968 move_pattern != MV_TURNING_LEFT_RIGHT &&
7969 move_pattern != MV_TURNING_RIGHT_LEFT &&
7970 move_pattern != MV_TURNING_RANDOM)
7974 if (MovDelay[x][y] && (element == EL_BUG ||
7975 element == EL_SPACESHIP ||
7976 element == EL_SP_SNIKSNAK ||
7977 element == EL_SP_ELECTRON ||
7978 element == EL_MOLE))
7979 DrawLevelField(x, y);
7983 if (MovDelay[x][y]) /* wait some time before next movement */
7987 if (element == EL_ROBOT ||
7988 element == EL_YAMYAM ||
7989 element == EL_DARK_YAMYAM)
7991 DrawLevelElementAnimationIfNeeded(x, y, element);
7992 PlayLevelSoundAction(x, y, ACTION_WAITING);
7994 else if (element == EL_SP_ELECTRON)
7995 DrawLevelElementAnimationIfNeeded(x, y, element);
7996 else if (element == EL_DRAGON)
7999 int dir = MovDir[x][y];
8000 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8001 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
8002 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
8003 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
8004 dir == MV_UP ? IMG_FLAMES_1_UP :
8005 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8006 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8008 GfxAction[x][y] = ACTION_ATTACKING;
8010 if (IS_PLAYER(x, y))
8011 DrawPlayerField(x, y);
8013 DrawLevelField(x, y);
8015 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8017 for (i = 1; i <= 3; i++)
8019 int xx = x + i * dx;
8020 int yy = y + i * dy;
8021 int sx = SCREENX(xx);
8022 int sy = SCREENY(yy);
8023 int flame_graphic = graphic + (i - 1);
8025 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8030 int flamed = MovingOrBlocked2Element(xx, yy);
8034 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8036 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8037 RemoveMovingField(xx, yy);
8039 RemoveField(xx, yy);
8041 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8044 RemoveMovingField(xx, yy);
8047 ChangeDelay[xx][yy] = 0;
8049 Feld[xx][yy] = EL_FLAMES;
8051 if (IN_SCR_FIELD(sx, sy))
8053 DrawLevelFieldCrumbledSand(xx, yy);
8054 DrawGraphic(sx, sy, flame_graphic, frame);
8059 if (Feld[xx][yy] == EL_FLAMES)
8060 Feld[xx][yy] = EL_EMPTY;
8061 DrawLevelField(xx, yy);
8066 if (MovDelay[x][y]) /* element still has to wait some time */
8068 PlayLevelSoundAction(x, y, ACTION_WAITING);
8074 /* now make next step */
8076 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8078 if (DONT_COLLIDE_WITH(element) &&
8079 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8080 !PLAYER_ENEMY_PROTECTED(newx, newy))
8082 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8087 else if (CAN_MOVE_INTO_ACID(element) &&
8088 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8089 !IS_MV_DIAGONAL(MovDir[x][y]) &&
8090 (MovDir[x][y] == MV_DOWN ||
8091 game.engine_version >= VERSION_IDENT(3,1,0,0)))
8093 SplashAcid(newx, newy);
8094 Store[x][y] = EL_ACID;
8096 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8098 if (Feld[newx][newy] == EL_EXIT_OPEN ||
8099 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8100 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8101 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8104 DrawLevelField(x, y);
8106 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8107 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8108 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8110 local_player->friends_still_needed--;
8111 if (!local_player->friends_still_needed &&
8112 !local_player->GameOver && AllPlayersGone)
8113 PlayerWins(local_player);
8117 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8119 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8120 DrawLevelField(newx, newy);
8122 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8124 else if (!IS_FREE(newx, newy))
8126 GfxAction[x][y] = ACTION_WAITING;
8128 if (IS_PLAYER(x, y))
8129 DrawPlayerField(x, y);
8131 DrawLevelField(x, y);
8136 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8138 if (IS_FOOD_PIG(Feld[newx][newy]))
8140 if (IS_MOVING(newx, newy))
8141 RemoveMovingField(newx, newy);
8144 Feld[newx][newy] = EL_EMPTY;
8145 DrawLevelField(newx, newy);
8148 PlayLevelSound(x, y, SND_PIG_DIGGING);
8150 else if (!IS_FREE(newx, newy))
8152 if (IS_PLAYER(x, y))
8153 DrawPlayerField(x, y);
8155 DrawLevelField(x, y);
8160 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8162 if (Store[x][y] != EL_EMPTY)
8164 boolean can_clone = FALSE;
8167 /* check if element to clone is still there */
8168 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8170 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8178 /* cannot clone or target field not free anymore -- do not clone */
8179 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8180 Store[x][y] = EL_EMPTY;
8183 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8185 if (IS_MV_DIAGONAL(MovDir[x][y]))
8187 int diagonal_move_dir = MovDir[x][y];
8188 int stored = Store[x][y];
8189 int change_delay = 8;
8192 /* android is moving diagonally */
8194 CreateField(x, y, EL_DIAGONAL_SHRINKING);
8196 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8197 GfxElement[x][y] = EL_EMC_ANDROID;
8198 GfxAction[x][y] = ACTION_SHRINKING;
8199 GfxDir[x][y] = diagonal_move_dir;
8200 ChangeDelay[x][y] = change_delay;
8202 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8205 DrawLevelGraphicAnimation(x, y, graphic);
8206 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8208 if (Feld[newx][newy] == EL_ACID)
8210 SplashAcid(newx, newy);
8215 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8217 Store[newx][newy] = EL_EMC_ANDROID;
8218 GfxElement[newx][newy] = EL_EMC_ANDROID;
8219 GfxAction[newx][newy] = ACTION_GROWING;
8220 GfxDir[newx][newy] = diagonal_move_dir;
8221 ChangeDelay[newx][newy] = change_delay;
8223 graphic = el_act_dir2img(GfxElement[newx][newy],
8224 GfxAction[newx][newy], GfxDir[newx][newy]);
8226 DrawLevelGraphicAnimation(newx, newy, graphic);
8227 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8233 Feld[newx][newy] = EL_EMPTY;
8234 DrawLevelField(newx, newy);
8236 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8239 else if (!IS_FREE(newx, newy))
8242 if (IS_PLAYER(x, y))
8243 DrawPlayerField(x, y);
8245 DrawLevelField(x, y);
8251 else if (IS_CUSTOM_ELEMENT(element) &&
8252 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8254 int new_element = Feld[newx][newy];
8256 if (!IS_FREE(newx, newy))
8258 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8259 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8262 /* no element can dig solid indestructible elements */
8263 if (IS_INDESTRUCTIBLE(new_element) &&
8264 !IS_DIGGABLE(new_element) &&
8265 !IS_COLLECTIBLE(new_element))
8268 if (AmoebaNr[newx][newy] &&
8269 (new_element == EL_AMOEBA_FULL ||
8270 new_element == EL_BD_AMOEBA ||
8271 new_element == EL_AMOEBA_GROWING))
8273 AmoebaCnt[AmoebaNr[newx][newy]]--;
8274 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8277 if (IS_MOVING(newx, newy))
8278 RemoveMovingField(newx, newy);
8281 RemoveField(newx, newy);
8282 DrawLevelField(newx, newy);
8285 /* if digged element was about to explode, prevent the explosion */
8286 ExplodeField[newx][newy] = EX_TYPE_NONE;
8288 PlayLevelSoundAction(x, y, action);
8291 Store[newx][newy] = EL_EMPTY;
8293 /* this makes it possible to leave the removed element again */
8294 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8295 Store[newx][newy] = new_element;
8297 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8299 int move_leave_element = element_info[element].move_leave_element;
8301 /* this makes it possible to leave the removed element again */
8302 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8303 new_element : move_leave_element);
8307 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8309 RunnerVisit[x][y] = FrameCounter;
8310 PlayerVisit[x][y] /= 8; /* expire player visit path */
8313 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8315 if (!IS_FREE(newx, newy))
8317 if (IS_PLAYER(x, y))
8318 DrawPlayerField(x, y);
8320 DrawLevelField(x, y);
8326 boolean wanna_flame = !RND(10);
8327 int dx = newx - x, dy = newy - y;
8328 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8329 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8330 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8331 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8332 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8333 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8336 IS_CLASSIC_ENEMY(element1) ||
8337 IS_CLASSIC_ENEMY(element2)) &&
8338 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8339 element1 != EL_FLAMES && element2 != EL_FLAMES)
8341 ResetGfxAnimation(x, y);
8342 GfxAction[x][y] = ACTION_ATTACKING;
8344 if (IS_PLAYER(x, y))
8345 DrawPlayerField(x, y);
8347 DrawLevelField(x, y);
8349 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8351 MovDelay[x][y] = 50;
8355 RemoveField(newx, newy);
8357 Feld[newx][newy] = EL_FLAMES;
8358 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8361 RemoveField(newx1, newy1);
8363 Feld[newx1][newy1] = EL_FLAMES;
8365 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8368 RemoveField(newx2, newy2);
8370 Feld[newx2][newy2] = EL_FLAMES;
8377 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8378 Feld[newx][newy] == EL_DIAMOND)
8380 if (IS_MOVING(newx, newy))
8381 RemoveMovingField(newx, newy);
8384 Feld[newx][newy] = EL_EMPTY;
8385 DrawLevelField(newx, newy);
8388 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8390 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8391 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8393 if (AmoebaNr[newx][newy])
8395 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8396 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8397 Feld[newx][newy] == EL_BD_AMOEBA)
8398 AmoebaCnt[AmoebaNr[newx][newy]]--;
8403 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8405 RemoveMovingField(newx, newy);
8408 if (IS_MOVING(newx, newy))
8410 RemoveMovingField(newx, newy);
8415 Feld[newx][newy] = EL_EMPTY;
8416 DrawLevelField(newx, newy);
8419 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8421 else if ((element == EL_PACMAN || element == EL_MOLE)
8422 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8424 if (AmoebaNr[newx][newy])
8426 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8427 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8428 Feld[newx][newy] == EL_BD_AMOEBA)
8429 AmoebaCnt[AmoebaNr[newx][newy]]--;
8432 if (element == EL_MOLE)
8434 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8435 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8437 ResetGfxAnimation(x, y);
8438 GfxAction[x][y] = ACTION_DIGGING;
8439 DrawLevelField(x, y);
8441 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8443 return; /* wait for shrinking amoeba */
8445 else /* element == EL_PACMAN */
8447 Feld[newx][newy] = EL_EMPTY;
8448 DrawLevelField(newx, newy);
8449 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8452 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8453 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8454 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8456 /* wait for shrinking amoeba to completely disappear */
8459 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8461 /* object was running against a wall */
8466 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8467 if (move_pattern & MV_ANY_DIRECTION &&
8468 move_pattern == MovDir[x][y])
8470 int blocking_element =
8471 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8473 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8476 element = Feld[x][y]; /* element might have changed */
8480 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8481 DrawLevelElementAnimation(x, y, element);
8483 if (DONT_TOUCH(element))
8484 TestIfBadThingTouchesPlayer(x, y);
8489 InitMovingField(x, y, MovDir[x][y]);
8491 PlayLevelSoundAction(x, y, ACTION_MOVING);
8495 ContinueMoving(x, y);
8498 void ContinueMoving(int x, int y)
8500 int element = Feld[x][y];
8501 struct ElementInfo *ei = &element_info[element];
8502 int direction = MovDir[x][y];
8503 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8504 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8505 int newx = x + dx, newy = y + dy;
8506 int stored = Store[x][y];
8507 int stored_new = Store[newx][newy];
8508 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
8509 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8510 boolean last_line = (newy == lev_fieldy - 1);
8512 MovPos[x][y] += getElementMoveStepsize(x, y);
8514 if (pushed_by_player) /* special case: moving object pushed by player */
8515 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8517 if (ABS(MovPos[x][y]) < TILEX)
8520 int ee = Feld[x][y];
8521 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8522 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8524 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8525 x, y, ABS(MovPos[x][y]),
8527 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8530 DrawLevelField(x, y);
8532 return; /* element is still moving */
8535 /* element reached destination field */
8537 Feld[x][y] = EL_EMPTY;
8538 Feld[newx][newy] = element;
8539 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8541 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
8543 element = Feld[newx][newy] = EL_ACID;
8545 else if (element == EL_MOLE)
8547 Feld[x][y] = EL_SAND;
8549 DrawLevelFieldCrumbledSandNeighbours(x, y);
8551 else if (element == EL_QUICKSAND_FILLING)
8553 element = Feld[newx][newy] = get_next_element(element);
8554 Store[newx][newy] = Store[x][y];
8556 else if (element == EL_QUICKSAND_EMPTYING)
8558 Feld[x][y] = get_next_element(element);
8559 element = Feld[newx][newy] = Store[x][y];
8561 else if (element == EL_QUICKSAND_FAST_FILLING)
8563 element = Feld[newx][newy] = get_next_element(element);
8564 Store[newx][newy] = Store[x][y];
8566 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8568 Feld[x][y] = get_next_element(element);
8569 element = Feld[newx][newy] = Store[x][y];
8571 else if (element == EL_MAGIC_WALL_FILLING)
8573 element = Feld[newx][newy] = get_next_element(element);
8574 if (!game.magic_wall_active)
8575 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8576 Store[newx][newy] = Store[x][y];
8578 else if (element == EL_MAGIC_WALL_EMPTYING)
8580 Feld[x][y] = get_next_element(element);
8581 if (!game.magic_wall_active)
8582 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8583 element = Feld[newx][newy] = Store[x][y];
8585 #if USE_NEW_CUSTOM_VALUE
8586 InitField(newx, newy, FALSE);
8589 else if (element == EL_BD_MAGIC_WALL_FILLING)
8591 element = Feld[newx][newy] = get_next_element(element);
8592 if (!game.magic_wall_active)
8593 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8594 Store[newx][newy] = Store[x][y];
8596 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8598 Feld[x][y] = get_next_element(element);
8599 if (!game.magic_wall_active)
8600 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8601 element = Feld[newx][newy] = Store[x][y];
8603 #if USE_NEW_CUSTOM_VALUE
8604 InitField(newx, newy, FALSE);
8607 else if (element == EL_DC_MAGIC_WALL_FILLING)
8609 element = Feld[newx][newy] = get_next_element(element);
8610 if (!game.magic_wall_active)
8611 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8612 Store[newx][newy] = Store[x][y];
8614 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8616 Feld[x][y] = get_next_element(element);
8617 if (!game.magic_wall_active)
8618 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8619 element = Feld[newx][newy] = Store[x][y];
8621 #if USE_NEW_CUSTOM_VALUE
8622 InitField(newx, newy, FALSE);
8625 else if (element == EL_AMOEBA_DROPPING)
8627 Feld[x][y] = get_next_element(element);
8628 element = Feld[newx][newy] = Store[x][y];
8630 else if (element == EL_SOKOBAN_OBJECT)
8633 Feld[x][y] = Back[x][y];
8635 if (Back[newx][newy])
8636 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8638 Back[x][y] = Back[newx][newy] = 0;
8641 Store[x][y] = EL_EMPTY;
8646 MovDelay[newx][newy] = 0;
8648 if (CAN_CHANGE_OR_HAS_ACTION(element))
8650 /* copy element change control values to new field */
8651 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8652 ChangePage[newx][newy] = ChangePage[x][y];
8653 ChangeCount[newx][newy] = ChangeCount[x][y];
8654 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8657 #if USE_NEW_CUSTOM_VALUE
8658 CustomValue[newx][newy] = CustomValue[x][y];
8661 ChangeDelay[x][y] = 0;
8662 ChangePage[x][y] = -1;
8663 ChangeCount[x][y] = 0;
8664 ChangeEvent[x][y] = -1;
8666 #if USE_NEW_CUSTOM_VALUE
8667 CustomValue[x][y] = 0;
8670 /* copy animation control values to new field */
8671 GfxFrame[newx][newy] = GfxFrame[x][y];
8672 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
8673 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
8674 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
8676 Pushed[x][y] = Pushed[newx][newy] = FALSE;
8678 /* some elements can leave other elements behind after moving */
8680 if (ei->move_leave_element != EL_EMPTY &&
8681 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8682 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8684 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
8685 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8686 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8689 int move_leave_element = ei->move_leave_element;
8693 /* this makes it possible to leave the removed element again */
8694 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8695 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8697 /* this makes it possible to leave the removed element again */
8698 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8699 move_leave_element = stored;
8702 /* this makes it possible to leave the removed element again */
8703 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8704 ei->move_leave_element == EL_TRIGGER_ELEMENT)
8705 move_leave_element = stored;
8708 Feld[x][y] = move_leave_element;
8710 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8711 MovDir[x][y] = direction;
8713 InitField(x, y, FALSE);
8715 if (GFX_CRUMBLED(Feld[x][y]))
8716 DrawLevelFieldCrumbledSandNeighbours(x, y);
8718 if (ELEM_IS_PLAYER(move_leave_element))
8719 RelocatePlayer(x, y, move_leave_element);
8722 /* do this after checking for left-behind element */
8723 ResetGfxAnimation(x, y); /* reset animation values for old field */
8725 if (!CAN_MOVE(element) ||
8726 (CAN_FALL(element) && direction == MV_DOWN &&
8727 (element == EL_SPRING ||
8728 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8729 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8730 GfxDir[x][y] = MovDir[newx][newy] = 0;
8732 DrawLevelField(x, y);
8733 DrawLevelField(newx, newy);
8735 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8737 /* prevent pushed element from moving on in pushed direction */
8738 if (pushed_by_player && CAN_MOVE(element) &&
8739 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8740 !(element_info[element].move_pattern & direction))
8741 TurnRound(newx, newy);
8743 /* prevent elements on conveyor belt from moving on in last direction */
8744 if (pushed_by_conveyor && CAN_FALL(element) &&
8745 direction & MV_HORIZONTAL)
8746 MovDir[newx][newy] = 0;
8748 if (!pushed_by_player)
8750 int nextx = newx + dx, nexty = newy + dy;
8751 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8753 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8755 if (CAN_FALL(element) && direction == MV_DOWN)
8756 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8758 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8759 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8761 #if USE_FIX_IMPACT_COLLISION
8762 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8763 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8767 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8769 TestIfBadThingTouchesPlayer(newx, newy);
8770 TestIfBadThingTouchesFriend(newx, newy);
8772 if (!IS_CUSTOM_ELEMENT(element))
8773 TestIfBadThingTouchesOtherBadThing(newx, newy);
8775 else if (element == EL_PENGUIN)
8776 TestIfFriendTouchesBadThing(newx, newy);
8778 /* give the player one last chance (one more frame) to move away */
8779 if (CAN_FALL(element) && direction == MV_DOWN &&
8780 (last_line || (!IS_FREE(x, newy + 1) &&
8781 (!IS_PLAYER(x, newy + 1) ||
8782 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8785 if (pushed_by_player && !game.use_change_when_pushing_bug)
8787 int push_side = MV_DIR_OPPOSITE(direction);
8788 struct PlayerInfo *player = PLAYERINFO(x, y);
8790 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8791 player->index_bit, push_side);
8792 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8793 player->index_bit, push_side);
8796 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
8797 MovDelay[newx][newy] = 1;
8799 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8801 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
8804 if (ChangePage[newx][newy] != -1) /* delayed change */
8806 int page = ChangePage[newx][newy];
8807 struct ElementChangeInfo *change = &ei->change_page[page];
8809 ChangePage[newx][newy] = -1;
8811 if (change->can_change)
8813 if (ChangeElement(newx, newy, element, page))
8815 if (change->post_change_function)
8816 change->post_change_function(newx, newy);
8820 if (change->has_action)
8821 ExecuteCustomElementAction(newx, newy, element, page);
8825 TestIfElementHitsCustomElement(newx, newy, direction);
8826 TestIfPlayerTouchesCustomElement(newx, newy);
8827 TestIfElementTouchesCustomElement(newx, newy);
8829 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8830 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8831 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8832 MV_DIR_OPPOSITE(direction));
8835 int AmoebeNachbarNr(int ax, int ay)
8838 int element = Feld[ax][ay];
8840 static int xy[4][2] =
8848 for (i = 0; i < NUM_DIRECTIONS; i++)
8850 int x = ax + xy[i][0];
8851 int y = ay + xy[i][1];
8853 if (!IN_LEV_FIELD(x, y))
8856 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8857 group_nr = AmoebaNr[x][y];
8863 void AmoebenVereinigen(int ax, int ay)
8865 int i, x, y, xx, yy;
8866 int new_group_nr = AmoebaNr[ax][ay];
8867 static int xy[4][2] =
8875 if (new_group_nr == 0)
8878 for (i = 0; i < NUM_DIRECTIONS; i++)
8883 if (!IN_LEV_FIELD(x, y))
8886 if ((Feld[x][y] == EL_AMOEBA_FULL ||
8887 Feld[x][y] == EL_BD_AMOEBA ||
8888 Feld[x][y] == EL_AMOEBA_DEAD) &&
8889 AmoebaNr[x][y] != new_group_nr)
8891 int old_group_nr = AmoebaNr[x][y];
8893 if (old_group_nr == 0)
8896 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8897 AmoebaCnt[old_group_nr] = 0;
8898 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8899 AmoebaCnt2[old_group_nr] = 0;
8901 SCAN_PLAYFIELD(xx, yy)
8903 if (AmoebaNr[xx][yy] == old_group_nr)
8904 AmoebaNr[xx][yy] = new_group_nr;
8910 void AmoebeUmwandeln(int ax, int ay)
8914 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8916 int group_nr = AmoebaNr[ax][ay];
8921 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8922 printf("AmoebeUmwandeln(): This should never happen!\n");
8927 SCAN_PLAYFIELD(x, y)
8929 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8932 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8936 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8937 SND_AMOEBA_TURNING_TO_GEM :
8938 SND_AMOEBA_TURNING_TO_ROCK));
8943 static int xy[4][2] =
8951 for (i = 0; i < NUM_DIRECTIONS; i++)
8956 if (!IN_LEV_FIELD(x, y))
8959 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8961 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8962 SND_AMOEBA_TURNING_TO_GEM :
8963 SND_AMOEBA_TURNING_TO_ROCK));
8970 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8973 int group_nr = AmoebaNr[ax][ay];
8974 boolean done = FALSE;
8979 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8980 printf("AmoebeUmwandelnBD(): This should never happen!\n");
8985 SCAN_PLAYFIELD(x, y)
8987 if (AmoebaNr[x][y] == group_nr &&
8988 (Feld[x][y] == EL_AMOEBA_DEAD ||
8989 Feld[x][y] == EL_BD_AMOEBA ||
8990 Feld[x][y] == EL_AMOEBA_GROWING))
8993 Feld[x][y] = new_element;
8994 InitField(x, y, FALSE);
8995 DrawLevelField(x, y);
9001 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9002 SND_BD_AMOEBA_TURNING_TO_ROCK :
9003 SND_BD_AMOEBA_TURNING_TO_GEM));
9006 void AmoebeWaechst(int x, int y)
9008 static unsigned long sound_delay = 0;
9009 static unsigned long sound_delay_value = 0;
9011 if (!MovDelay[x][y]) /* start new growing cycle */
9015 if (DelayReached(&sound_delay, sound_delay_value))
9017 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9018 sound_delay_value = 30;
9022 if (MovDelay[x][y]) /* wait some time before growing bigger */
9025 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9027 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9028 6 - MovDelay[x][y]);
9030 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9033 if (!MovDelay[x][y])
9035 Feld[x][y] = Store[x][y];
9037 DrawLevelField(x, y);
9042 void AmoebaDisappearing(int x, int y)
9044 static unsigned long sound_delay = 0;
9045 static unsigned long sound_delay_value = 0;
9047 if (!MovDelay[x][y]) /* start new shrinking cycle */
9051 if (DelayReached(&sound_delay, sound_delay_value))
9052 sound_delay_value = 30;
9055 if (MovDelay[x][y]) /* wait some time before shrinking */
9058 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9060 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9061 6 - MovDelay[x][y]);
9063 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9066 if (!MovDelay[x][y])
9068 Feld[x][y] = EL_EMPTY;
9069 DrawLevelField(x, y);
9071 /* don't let mole enter this field in this cycle;
9072 (give priority to objects falling to this field from above) */
9078 void AmoebeAbleger(int ax, int ay)
9081 int element = Feld[ax][ay];
9082 int graphic = el2img(element);
9083 int newax = ax, neway = ay;
9084 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9085 static int xy[4][2] =
9093 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9095 Feld[ax][ay] = EL_AMOEBA_DEAD;
9096 DrawLevelField(ax, ay);
9100 if (IS_ANIMATED(graphic))
9101 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9103 if (!MovDelay[ax][ay]) /* start making new amoeba field */
9104 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9106 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
9109 if (MovDelay[ax][ay])
9113 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9116 int x = ax + xy[start][0];
9117 int y = ay + xy[start][1];
9119 if (!IN_LEV_FIELD(x, y))
9122 if (IS_FREE(x, y) ||
9123 CAN_GROW_INTO(Feld[x][y]) ||
9124 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9125 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9131 if (newax == ax && neway == ay)
9134 else /* normal or "filled" (BD style) amoeba */
9137 boolean waiting_for_player = FALSE;
9139 for (i = 0; i < NUM_DIRECTIONS; i++)
9141 int j = (start + i) % 4;
9142 int x = ax + xy[j][0];
9143 int y = ay + xy[j][1];
9145 if (!IN_LEV_FIELD(x, y))
9148 if (IS_FREE(x, y) ||
9149 CAN_GROW_INTO(Feld[x][y]) ||
9150 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9151 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9157 else if (IS_PLAYER(x, y))
9158 waiting_for_player = TRUE;
9161 if (newax == ax && neway == ay) /* amoeba cannot grow */
9163 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9165 Feld[ax][ay] = EL_AMOEBA_DEAD;
9166 DrawLevelField(ax, ay);
9167 AmoebaCnt[AmoebaNr[ax][ay]]--;
9169 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
9171 if (element == EL_AMOEBA_FULL)
9172 AmoebeUmwandeln(ax, ay);
9173 else if (element == EL_BD_AMOEBA)
9174 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9179 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9181 /* amoeba gets larger by growing in some direction */
9183 int new_group_nr = AmoebaNr[ax][ay];
9186 if (new_group_nr == 0)
9188 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9189 printf("AmoebeAbleger(): This should never happen!\n");
9194 AmoebaNr[newax][neway] = new_group_nr;
9195 AmoebaCnt[new_group_nr]++;
9196 AmoebaCnt2[new_group_nr]++;
9198 /* if amoeba touches other amoeba(s) after growing, unify them */
9199 AmoebenVereinigen(newax, neway);
9201 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9203 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9209 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9210 (neway == lev_fieldy - 1 && newax != ax))
9212 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
9213 Store[newax][neway] = element;
9215 else if (neway == ay || element == EL_EMC_DRIPPER)
9217 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
9219 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9223 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
9224 Feld[ax][ay] = EL_AMOEBA_DROPPING;
9225 Store[ax][ay] = EL_AMOEBA_DROP;
9226 ContinueMoving(ax, ay);
9230 DrawLevelField(newax, neway);
9233 void Life(int ax, int ay)
9237 int element = Feld[ax][ay];
9238 int graphic = el2img(element);
9239 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9241 boolean changed = FALSE;
9243 if (IS_ANIMATED(graphic))
9244 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9249 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
9250 MovDelay[ax][ay] = life_time;
9252 if (MovDelay[ax][ay]) /* wait some time before next cycle */
9255 if (MovDelay[ax][ay])
9259 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9261 int xx = ax+x1, yy = ay+y1;
9264 if (!IN_LEV_FIELD(xx, yy))
9267 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9269 int x = xx+x2, y = yy+y2;
9271 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9274 if (((Feld[x][y] == element ||
9275 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9277 (IS_FREE(x, y) && Stop[x][y]))
9281 if (xx == ax && yy == ay) /* field in the middle */
9283 if (nachbarn < life_parameter[0] ||
9284 nachbarn > life_parameter[1])
9286 Feld[xx][yy] = EL_EMPTY;
9288 DrawLevelField(xx, yy);
9289 Stop[xx][yy] = TRUE;
9293 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9294 { /* free border field */
9295 if (nachbarn >= life_parameter[2] &&
9296 nachbarn <= life_parameter[3])
9298 Feld[xx][yy] = element;
9299 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9301 DrawLevelField(xx, yy);
9302 Stop[xx][yy] = TRUE;
9309 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9310 SND_GAME_OF_LIFE_GROWING);
9313 static void InitRobotWheel(int x, int y)
9315 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9318 static void RunRobotWheel(int x, int y)
9320 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9323 static void StopRobotWheel(int x, int y)
9325 if (ZX == x && ZY == y)
9329 game.robot_wheel_active = FALSE;
9333 static void InitTimegateWheel(int x, int y)
9335 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9338 static void RunTimegateWheel(int x, int y)
9340 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9343 static void InitMagicBallDelay(int x, int y)
9346 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9348 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9352 static void ActivateMagicBall(int bx, int by)
9356 if (level.ball_random)
9358 int pos_border = RND(8); /* select one of the eight border elements */
9359 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9360 int xx = pos_content % 3;
9361 int yy = pos_content / 3;
9366 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9367 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9371 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9373 int xx = x - bx + 1;
9374 int yy = y - by + 1;
9376 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9377 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9381 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9384 void CheckExit(int x, int y)
9386 if (local_player->gems_still_needed > 0 ||
9387 local_player->sokobanfields_still_needed > 0 ||
9388 local_player->lights_still_needed > 0)
9390 int element = Feld[x][y];
9391 int graphic = el2img(element);
9393 if (IS_ANIMATED(graphic))
9394 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9399 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9402 Feld[x][y] = EL_EXIT_OPENING;
9404 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9407 void CheckExitEM(int x, int y)
9409 if (local_player->gems_still_needed > 0 ||
9410 local_player->sokobanfields_still_needed > 0 ||
9411 local_player->lights_still_needed > 0)
9413 int element = Feld[x][y];
9414 int graphic = el2img(element);
9416 if (IS_ANIMATED(graphic))
9417 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9422 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9425 Feld[x][y] = EL_EM_EXIT_OPENING;
9427 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9430 void CheckExitSteel(int x, int y)
9432 if (local_player->gems_still_needed > 0 ||
9433 local_player->sokobanfields_still_needed > 0 ||
9434 local_player->lights_still_needed > 0)
9436 int element = Feld[x][y];
9437 int graphic = el2img(element);
9439 if (IS_ANIMATED(graphic))
9440 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9445 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9448 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9450 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9453 void CheckExitSteelEM(int x, int y)
9455 if (local_player->gems_still_needed > 0 ||
9456 local_player->sokobanfields_still_needed > 0 ||
9457 local_player->lights_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_EM_STEEL_EXIT_OPENING;
9473 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9476 void CheckExitSP(int x, int y)
9478 if (local_player->gems_still_needed > 0)
9480 int element = Feld[x][y];
9481 int graphic = el2img(element);
9483 if (IS_ANIMATED(graphic))
9484 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9489 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9492 Feld[x][y] = EL_SP_EXIT_OPENING;
9494 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9497 static void CloseAllOpenTimegates()
9501 SCAN_PLAYFIELD(x, y)
9503 int element = Feld[x][y];
9505 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9507 Feld[x][y] = EL_TIMEGATE_CLOSING;
9509 PlayLevelSoundAction(x, y, ACTION_CLOSING);
9514 void DrawTwinkleOnField(int x, int y)
9516 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9519 if (Feld[x][y] == EL_BD_DIAMOND)
9522 if (MovDelay[x][y] == 0) /* next animation frame */
9523 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9525 if (MovDelay[x][y] != 0) /* wait some time before next frame */
9529 DrawLevelElementAnimation(x, y, Feld[x][y]);
9531 if (MovDelay[x][y] != 0)
9533 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9534 10 - MovDelay[x][y]);
9536 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9541 void MauerWaechst(int x, int y)
9545 if (!MovDelay[x][y]) /* next animation frame */
9546 MovDelay[x][y] = 3 * delay;
9548 if (MovDelay[x][y]) /* wait some time before next frame */
9552 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9554 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9555 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9557 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9560 if (!MovDelay[x][y])
9562 if (MovDir[x][y] == MV_LEFT)
9564 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9565 DrawLevelField(x - 1, y);
9567 else if (MovDir[x][y] == MV_RIGHT)
9569 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9570 DrawLevelField(x + 1, y);
9572 else if (MovDir[x][y] == MV_UP)
9574 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9575 DrawLevelField(x, y - 1);
9579 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9580 DrawLevelField(x, y + 1);
9583 Feld[x][y] = Store[x][y];
9585 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9586 DrawLevelField(x, y);
9591 void MauerAbleger(int ax, int ay)
9593 int element = Feld[ax][ay];
9594 int graphic = el2img(element);
9595 boolean oben_frei = FALSE, unten_frei = FALSE;
9596 boolean links_frei = FALSE, rechts_frei = FALSE;
9597 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9598 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9599 boolean new_wall = FALSE;
9601 if (IS_ANIMATED(graphic))
9602 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9604 if (!MovDelay[ax][ay]) /* start building new wall */
9605 MovDelay[ax][ay] = 6;
9607 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9610 if (MovDelay[ax][ay])
9614 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9616 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9618 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9620 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9623 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9624 element == EL_EXPANDABLE_WALL_ANY)
9628 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9629 Store[ax][ay-1] = element;
9630 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9631 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9632 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9633 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9638 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9639 Store[ax][ay+1] = element;
9640 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9641 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9642 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9643 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9648 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9649 element == EL_EXPANDABLE_WALL_ANY ||
9650 element == EL_EXPANDABLE_WALL ||
9651 element == EL_BD_EXPANDABLE_WALL)
9655 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9656 Store[ax-1][ay] = element;
9657 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9658 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9659 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9660 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9666 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9667 Store[ax+1][ay] = element;
9668 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9669 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9670 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9671 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9676 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9677 DrawLevelField(ax, ay);
9679 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9681 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9682 unten_massiv = TRUE;
9683 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9684 links_massiv = TRUE;
9685 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9686 rechts_massiv = TRUE;
9688 if (((oben_massiv && unten_massiv) ||
9689 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9690 element == EL_EXPANDABLE_WALL) &&
9691 ((links_massiv && rechts_massiv) ||
9692 element == EL_EXPANDABLE_WALL_VERTICAL))
9693 Feld[ax][ay] = EL_WALL;
9696 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9699 void MauerAblegerStahl(int ax, int ay)
9701 int element = Feld[ax][ay];
9702 int graphic = el2img(element);
9703 boolean oben_frei = FALSE, unten_frei = FALSE;
9704 boolean links_frei = FALSE, rechts_frei = FALSE;
9705 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9706 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9707 boolean new_wall = FALSE;
9709 if (IS_ANIMATED(graphic))
9710 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9712 if (!MovDelay[ax][ay]) /* start building new wall */
9713 MovDelay[ax][ay] = 6;
9715 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9718 if (MovDelay[ax][ay])
9722 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9724 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9726 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9728 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9731 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9732 element == EL_EXPANDABLE_STEELWALL_ANY)
9736 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9737 Store[ax][ay-1] = element;
9738 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9739 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9740 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9741 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9746 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9747 Store[ax][ay+1] = element;
9748 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9749 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9750 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9751 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9756 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9757 element == EL_EXPANDABLE_STEELWALL_ANY)
9761 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9762 Store[ax-1][ay] = element;
9763 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9764 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9765 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9766 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9772 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9773 Store[ax+1][ay] = element;
9774 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9775 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9776 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9777 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9782 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9784 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9785 unten_massiv = TRUE;
9786 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9787 links_massiv = TRUE;
9788 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9789 rechts_massiv = TRUE;
9791 if (((oben_massiv && unten_massiv) ||
9792 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9793 ((links_massiv && rechts_massiv) ||
9794 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9795 Feld[ax][ay] = EL_WALL;
9798 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9801 void CheckForDragon(int x, int y)
9804 boolean dragon_found = FALSE;
9805 static int xy[4][2] =
9813 for (i = 0; i < NUM_DIRECTIONS; i++)
9815 for (j = 0; j < 4; j++)
9817 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9819 if (IN_LEV_FIELD(xx, yy) &&
9820 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9822 if (Feld[xx][yy] == EL_DRAGON)
9823 dragon_found = TRUE;
9832 for (i = 0; i < NUM_DIRECTIONS; i++)
9834 for (j = 0; j < 3; j++)
9836 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9838 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9840 Feld[xx][yy] = EL_EMPTY;
9841 DrawLevelField(xx, yy);
9850 static void InitBuggyBase(int x, int y)
9852 int element = Feld[x][y];
9853 int activating_delay = FRAMES_PER_SECOND / 4;
9856 (element == EL_SP_BUGGY_BASE ?
9857 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9858 element == EL_SP_BUGGY_BASE_ACTIVATING ?
9860 element == EL_SP_BUGGY_BASE_ACTIVE ?
9861 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9864 static void WarnBuggyBase(int x, int y)
9867 static int xy[4][2] =
9875 for (i = 0; i < NUM_DIRECTIONS; i++)
9877 int xx = x + xy[i][0];
9878 int yy = y + xy[i][1];
9880 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9882 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9889 static void InitTrap(int x, int y)
9891 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9894 static void ActivateTrap(int x, int y)
9896 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9899 static void ChangeActiveTrap(int x, int y)
9901 int graphic = IMG_TRAP_ACTIVE;
9903 /* if new animation frame was drawn, correct crumbled sand border */
9904 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9905 DrawLevelFieldCrumbledSand(x, y);
9908 static int getSpecialActionElement(int element, int number, int base_element)
9910 return (element != EL_EMPTY ? element :
9911 number != -1 ? base_element + number - 1 :
9915 static int getModifiedActionNumber(int value_old, int operator, int operand,
9916 int value_min, int value_max)
9918 int value_new = (operator == CA_MODE_SET ? operand :
9919 operator == CA_MODE_ADD ? value_old + operand :
9920 operator == CA_MODE_SUBTRACT ? value_old - operand :
9921 operator == CA_MODE_MULTIPLY ? value_old * operand :
9922 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
9923 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
9926 return (value_new < value_min ? value_min :
9927 value_new > value_max ? value_max :
9931 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9933 struct ElementInfo *ei = &element_info[element];
9934 struct ElementChangeInfo *change = &ei->change_page[page];
9935 int target_element = change->target_element;
9936 int action_type = change->action_type;
9937 int action_mode = change->action_mode;
9938 int action_arg = change->action_arg;
9941 if (!change->has_action)
9944 /* ---------- determine action paramater values -------------------------- */
9946 int level_time_value =
9947 (level.time > 0 ? TimeLeft :
9950 int action_arg_element =
9951 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
9952 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9953 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
9956 int action_arg_direction =
9957 (action_arg >= CA_ARG_DIRECTION_LEFT &&
9958 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9959 action_arg == CA_ARG_DIRECTION_TRIGGER ?
9960 change->actual_trigger_side :
9961 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9962 MV_DIR_OPPOSITE(change->actual_trigger_side) :
9965 int action_arg_number_min =
9966 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9969 int action_arg_number_max =
9970 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9971 action_type == CA_SET_LEVEL_GEMS ? 999 :
9972 action_type == CA_SET_LEVEL_TIME ? 9999 :
9973 action_type == CA_SET_LEVEL_SCORE ? 99999 :
9974 action_type == CA_SET_CE_VALUE ? 9999 :
9975 action_type == CA_SET_CE_SCORE ? 9999 :
9978 int action_arg_number_reset =
9979 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9980 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9981 action_type == CA_SET_LEVEL_TIME ? level.time :
9982 action_type == CA_SET_LEVEL_SCORE ? 0 :
9983 #if USE_NEW_CUSTOM_VALUE
9984 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9986 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
9988 action_type == CA_SET_CE_SCORE ? 0 :
9991 int action_arg_number =
9992 (action_arg <= CA_ARG_MAX ? action_arg :
9993 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9994 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9995 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9996 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9997 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9998 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9999 #if USE_NEW_CUSTOM_VALUE
10000 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10002 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10004 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10005 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10006 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10007 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10008 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10009 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10010 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10011 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10012 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10013 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
10014 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10017 int action_arg_number_old =
10018 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10019 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10020 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10021 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10022 action_type == CA_SET_CE_SCORE ? ei->collect_score :
10025 int action_arg_number_new =
10026 getModifiedActionNumber(action_arg_number_old,
10027 action_mode, action_arg_number,
10028 action_arg_number_min, action_arg_number_max);
10031 int trigger_player_bits = change->actual_trigger_player_bits;
10033 int trigger_player_bits =
10034 (change->actual_trigger_player >= EL_PLAYER_1 &&
10035 change->actual_trigger_player <= EL_PLAYER_4 ?
10036 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10040 int action_arg_player_bits =
10041 (action_arg >= CA_ARG_PLAYER_1 &&
10042 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10043 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10046 /* ---------- execute action -------------------------------------------- */
10048 switch (action_type)
10055 /* ---------- level actions ------------------------------------------- */
10057 case CA_RESTART_LEVEL:
10059 game.restart_level = TRUE;
10064 case CA_SHOW_ENVELOPE:
10066 int element = getSpecialActionElement(action_arg_element,
10067 action_arg_number, EL_ENVELOPE_1);
10069 if (IS_ENVELOPE(element))
10070 local_player->show_envelope = element;
10075 case CA_SET_LEVEL_TIME:
10077 if (level.time > 0) /* only modify limited time value */
10079 TimeLeft = action_arg_number_new;
10082 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10084 DisplayGameControlValues();
10086 DrawGameValue_Time(TimeLeft);
10089 if (!TimeLeft && setup.time_limit)
10090 for (i = 0; i < MAX_PLAYERS; i++)
10091 KillPlayer(&stored_player[i]);
10097 case CA_SET_LEVEL_SCORE:
10099 local_player->score = action_arg_number_new;
10102 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10104 DisplayGameControlValues();
10106 DrawGameValue_Score(local_player->score);
10112 case CA_SET_LEVEL_GEMS:
10114 local_player->gems_still_needed = action_arg_number_new;
10117 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10119 DisplayGameControlValues();
10121 DrawGameValue_Emeralds(local_player->gems_still_needed);
10127 #if !USE_PLAYER_GRAVITY
10128 case CA_SET_LEVEL_GRAVITY:
10130 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10131 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10132 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10138 case CA_SET_LEVEL_WIND:
10140 game.wind_direction = action_arg_direction;
10145 /* ---------- player actions ------------------------------------------ */
10147 case CA_MOVE_PLAYER:
10149 /* automatically move to the next field in specified direction */
10150 for (i = 0; i < MAX_PLAYERS; i++)
10151 if (trigger_player_bits & (1 << i))
10152 stored_player[i].programmed_action = action_arg_direction;
10157 case CA_EXIT_PLAYER:
10159 for (i = 0; i < MAX_PLAYERS; i++)
10160 if (action_arg_player_bits & (1 << i))
10161 PlayerWins(&stored_player[i]);
10166 case CA_KILL_PLAYER:
10168 for (i = 0; i < MAX_PLAYERS; i++)
10169 if (action_arg_player_bits & (1 << i))
10170 KillPlayer(&stored_player[i]);
10175 case CA_SET_PLAYER_KEYS:
10177 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10178 int element = getSpecialActionElement(action_arg_element,
10179 action_arg_number, EL_KEY_1);
10181 if (IS_KEY(element))
10183 for (i = 0; i < MAX_PLAYERS; i++)
10185 if (trigger_player_bits & (1 << i))
10187 stored_player[i].key[KEY_NR(element)] = key_state;
10189 DrawGameDoorValues();
10197 case CA_SET_PLAYER_SPEED:
10199 for (i = 0; i < MAX_PLAYERS; i++)
10201 if (trigger_player_bits & (1 << i))
10203 int move_stepsize = TILEX / stored_player[i].move_delay_value;
10205 if (action_arg == CA_ARG_SPEED_FASTER &&
10206 stored_player[i].cannot_move)
10208 action_arg_number = STEPSIZE_VERY_SLOW;
10210 else if (action_arg == CA_ARG_SPEED_SLOWER ||
10211 action_arg == CA_ARG_SPEED_FASTER)
10213 action_arg_number = 2;
10214 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10217 else if (action_arg == CA_ARG_NUMBER_RESET)
10219 action_arg_number = level.initial_player_stepsize[i];
10223 getModifiedActionNumber(move_stepsize,
10226 action_arg_number_min,
10227 action_arg_number_max);
10229 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10236 case CA_SET_PLAYER_SHIELD:
10238 for (i = 0; i < MAX_PLAYERS; i++)
10240 if (trigger_player_bits & (1 << i))
10242 if (action_arg == CA_ARG_SHIELD_OFF)
10244 stored_player[i].shield_normal_time_left = 0;
10245 stored_player[i].shield_deadly_time_left = 0;
10247 else if (action_arg == CA_ARG_SHIELD_NORMAL)
10249 stored_player[i].shield_normal_time_left = 999999;
10251 else if (action_arg == CA_ARG_SHIELD_DEADLY)
10253 stored_player[i].shield_normal_time_left = 999999;
10254 stored_player[i].shield_deadly_time_left = 999999;
10262 #if USE_PLAYER_GRAVITY
10263 case CA_SET_PLAYER_GRAVITY:
10265 for (i = 0; i < MAX_PLAYERS; i++)
10267 if (trigger_player_bits & (1 << i))
10269 stored_player[i].gravity =
10270 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10271 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10272 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10273 stored_player[i].gravity);
10281 case CA_SET_PLAYER_ARTWORK:
10283 for (i = 0; i < MAX_PLAYERS; i++)
10285 if (trigger_player_bits & (1 << i))
10287 int artwork_element = action_arg_element;
10289 if (action_arg == CA_ARG_ELEMENT_RESET)
10291 (level.use_artwork_element[i] ? level.artwork_element[i] :
10292 stored_player[i].element_nr);
10294 #if USE_GFX_RESET_PLAYER_ARTWORK
10295 if (stored_player[i].artwork_element != artwork_element)
10296 stored_player[i].Frame = 0;
10299 stored_player[i].artwork_element = artwork_element;
10301 SetPlayerWaiting(&stored_player[i], FALSE);
10303 /* set number of special actions for bored and sleeping animation */
10304 stored_player[i].num_special_action_bored =
10305 get_num_special_action(artwork_element,
10306 ACTION_BORING_1, ACTION_BORING_LAST);
10307 stored_player[i].num_special_action_sleeping =
10308 get_num_special_action(artwork_element,
10309 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10316 /* ---------- CE actions ---------------------------------------------- */
10318 case CA_SET_CE_VALUE:
10320 #if USE_NEW_CUSTOM_VALUE
10321 int last_ce_value = CustomValue[x][y];
10323 CustomValue[x][y] = action_arg_number_new;
10325 if (CustomValue[x][y] != last_ce_value)
10327 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10328 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10330 if (CustomValue[x][y] == 0)
10332 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10333 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10341 case CA_SET_CE_SCORE:
10343 #if USE_NEW_CUSTOM_VALUE
10344 int last_ce_score = ei->collect_score;
10346 ei->collect_score = action_arg_number_new;
10348 if (ei->collect_score != last_ce_score)
10350 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10351 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10353 if (ei->collect_score == 0)
10357 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10358 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10361 This is a very special case that seems to be a mixture between
10362 CheckElementChange() and CheckTriggeredElementChange(): while
10363 the first one only affects single elements that are triggered
10364 directly, the second one affects multiple elements in the playfield
10365 that are triggered indirectly by another element. This is a third
10366 case: Changing the CE score always affects multiple identical CEs,
10367 so every affected CE must be checked, not only the single CE for
10368 which the CE score was changed in the first place (as every instance
10369 of that CE shares the same CE score, and therefore also can change)!
10371 SCAN_PLAYFIELD(xx, yy)
10373 if (Feld[xx][yy] == element)
10374 CheckElementChange(xx, yy, element, EL_UNDEFINED,
10375 CE_SCORE_GETS_ZERO);
10384 /* ---------- engine actions ------------------------------------------ */
10386 case CA_SET_ENGINE_SCAN_MODE:
10388 InitPlayfieldScanMode(action_arg);
10398 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10400 int old_element = Feld[x][y];
10401 int new_element = GetElementFromGroupElement(element);
10402 int previous_move_direction = MovDir[x][y];
10403 #if USE_NEW_CUSTOM_VALUE
10404 int last_ce_value = CustomValue[x][y];
10406 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10407 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10408 boolean add_player_onto_element = (new_element_is_player &&
10409 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
10410 /* this breaks SnakeBite when a snake is
10411 halfway through a door that closes */
10412 /* NOW FIXED AT LEVEL INIT IN files.c */
10413 new_element != EL_SOKOBAN_FIELD_PLAYER &&
10415 IS_WALKABLE(old_element));
10418 /* check if element under the player changes from accessible to unaccessible
10419 (needed for special case of dropping element which then changes) */
10420 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10421 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10429 if (!add_player_onto_element)
10431 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10432 RemoveMovingField(x, y);
10436 Feld[x][y] = new_element;
10438 #if !USE_GFX_RESET_GFX_ANIMATION
10439 ResetGfxAnimation(x, y);
10440 ResetRandomAnimationValue(x, y);
10443 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10444 MovDir[x][y] = previous_move_direction;
10446 #if USE_NEW_CUSTOM_VALUE
10447 if (element_info[new_element].use_last_ce_value)
10448 CustomValue[x][y] = last_ce_value;
10451 InitField_WithBug1(x, y, FALSE);
10453 new_element = Feld[x][y]; /* element may have changed */
10455 #if USE_GFX_RESET_GFX_ANIMATION
10456 ResetGfxAnimation(x, y);
10457 ResetRandomAnimationValue(x, y);
10460 DrawLevelField(x, y);
10462 if (GFX_CRUMBLED(new_element))
10463 DrawLevelFieldCrumbledSandNeighbours(x, y);
10467 /* check if element under the player changes from accessible to unaccessible
10468 (needed for special case of dropping element which then changes) */
10469 /* (must be checked after creating new element for walkable group elements) */
10470 #if USE_FIX_KILLED_BY_NON_WALKABLE
10471 if (IS_PLAYER(x, y) && !player_explosion_protected &&
10472 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10479 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10480 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10489 /* "ChangeCount" not set yet to allow "entered by player" change one time */
10490 if (new_element_is_player)
10491 RelocatePlayer(x, y, new_element);
10494 ChangeCount[x][y]++; /* count number of changes in the same frame */
10496 TestIfBadThingTouchesPlayer(x, y);
10497 TestIfPlayerTouchesCustomElement(x, y);
10498 TestIfElementTouchesCustomElement(x, y);
10501 static void CreateField(int x, int y, int element)
10503 CreateFieldExt(x, y, element, FALSE);
10506 static void CreateElementFromChange(int x, int y, int element)
10508 element = GET_VALID_RUNTIME_ELEMENT(element);
10510 #if USE_STOP_CHANGED_ELEMENTS
10511 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10513 int old_element = Feld[x][y];
10515 /* prevent changed element from moving in same engine frame
10516 unless both old and new element can either fall or move */
10517 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10518 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10523 CreateFieldExt(x, y, element, TRUE);
10526 static boolean ChangeElement(int x, int y, int element, int page)
10528 struct ElementInfo *ei = &element_info[element];
10529 struct ElementChangeInfo *change = &ei->change_page[page];
10530 int ce_value = CustomValue[x][y];
10531 int ce_score = ei->collect_score;
10532 int target_element;
10533 int old_element = Feld[x][y];
10535 /* always use default change event to prevent running into a loop */
10536 if (ChangeEvent[x][y] == -1)
10537 ChangeEvent[x][y] = CE_DELAY;
10539 if (ChangeEvent[x][y] == CE_DELAY)
10541 /* reset actual trigger element, trigger player and action element */
10542 change->actual_trigger_element = EL_EMPTY;
10543 change->actual_trigger_player = EL_PLAYER_1;
10544 change->actual_trigger_player_bits = CH_PLAYER_1;
10545 change->actual_trigger_side = CH_SIDE_NONE;
10546 change->actual_trigger_ce_value = 0;
10547 change->actual_trigger_ce_score = 0;
10550 /* do not change elements more than a specified maximum number of changes */
10551 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10554 ChangeCount[x][y]++; /* count number of changes in the same frame */
10556 if (change->explode)
10563 if (change->use_target_content)
10565 boolean complete_replace = TRUE;
10566 boolean can_replace[3][3];
10569 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10572 boolean is_walkable;
10573 boolean is_diggable;
10574 boolean is_collectible;
10575 boolean is_removable;
10576 boolean is_destructible;
10577 int ex = x + xx - 1;
10578 int ey = y + yy - 1;
10579 int content_element = change->target_content.e[xx][yy];
10582 can_replace[xx][yy] = TRUE;
10584 if (ex == x && ey == y) /* do not check changing element itself */
10587 if (content_element == EL_EMPTY_SPACE)
10589 can_replace[xx][yy] = FALSE; /* do not replace border with space */
10594 if (!IN_LEV_FIELD(ex, ey))
10596 can_replace[xx][yy] = FALSE;
10597 complete_replace = FALSE;
10604 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10605 e = MovingOrBlocked2Element(ex, ey);
10607 is_empty = (IS_FREE(ex, ey) ||
10608 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10610 is_walkable = (is_empty || IS_WALKABLE(e));
10611 is_diggable = (is_empty || IS_DIGGABLE(e));
10612 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10613 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10614 is_removable = (is_diggable || is_collectible);
10616 can_replace[xx][yy] =
10617 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10618 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10619 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10620 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10621 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10622 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10623 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10625 if (!can_replace[xx][yy])
10626 complete_replace = FALSE;
10629 if (!change->only_if_complete || complete_replace)
10631 boolean something_has_changed = FALSE;
10633 if (change->only_if_complete && change->use_random_replace &&
10634 RND(100) < change->random_percentage)
10637 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10639 int ex = x + xx - 1;
10640 int ey = y + yy - 1;
10641 int content_element;
10643 if (can_replace[xx][yy] && (!change->use_random_replace ||
10644 RND(100) < change->random_percentage))
10646 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10647 RemoveMovingField(ex, ey);
10649 ChangeEvent[ex][ey] = ChangeEvent[x][y];
10651 content_element = change->target_content.e[xx][yy];
10652 target_element = GET_TARGET_ELEMENT(element, content_element, change,
10653 ce_value, ce_score);
10655 CreateElementFromChange(ex, ey, target_element);
10657 something_has_changed = TRUE;
10659 /* for symmetry reasons, freeze newly created border elements */
10660 if (ex != x || ey != y)
10661 Stop[ex][ey] = TRUE; /* no more moving in this frame */
10665 if (something_has_changed)
10667 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10668 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10674 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10675 ce_value, ce_score);
10677 if (element == EL_DIAGONAL_GROWING ||
10678 element == EL_DIAGONAL_SHRINKING)
10680 target_element = Store[x][y];
10682 Store[x][y] = EL_EMPTY;
10685 CreateElementFromChange(x, y, target_element);
10687 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10688 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10691 /* this uses direct change before indirect change */
10692 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10697 #if USE_NEW_DELAYED_ACTION
10699 static void HandleElementChange(int x, int y, int page)
10701 int element = MovingOrBlocked2Element(x, y);
10702 struct ElementInfo *ei = &element_info[element];
10703 struct ElementChangeInfo *change = &ei->change_page[page];
10706 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10707 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10710 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10711 x, y, element, element_info[element].token_name);
10712 printf("HandleElementChange(): This should never happen!\n");
10717 /* this can happen with classic bombs on walkable, changing elements */
10718 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10721 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
10722 ChangeDelay[x][y] = 0;
10728 if (ChangeDelay[x][y] == 0) /* initialize element change */
10730 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10732 if (change->can_change)
10735 /* !!! not clear why graphic animation should be reset at all here !!! */
10736 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10737 #if USE_GFX_RESET_WHEN_NOT_MOVING
10738 /* when a custom element is about to change (for example by change delay),
10739 do not reset graphic animation when the custom element is moving */
10740 if (!IS_MOVING(x, y))
10743 ResetGfxAnimation(x, y);
10744 ResetRandomAnimationValue(x, y);
10748 if (change->pre_change_function)
10749 change->pre_change_function(x, y);
10753 ChangeDelay[x][y]--;
10755 if (ChangeDelay[x][y] != 0) /* continue element change */
10757 if (change->can_change)
10759 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10761 if (IS_ANIMATED(graphic))
10762 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10764 if (change->change_function)
10765 change->change_function(x, y);
10768 else /* finish element change */
10770 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10772 page = ChangePage[x][y];
10773 ChangePage[x][y] = -1;
10775 change = &ei->change_page[page];
10778 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10780 ChangeDelay[x][y] = 1; /* try change after next move step */
10781 ChangePage[x][y] = page; /* remember page to use for change */
10786 if (change->can_change)
10788 if (ChangeElement(x, y, element, page))
10790 if (change->post_change_function)
10791 change->post_change_function(x, y);
10795 if (change->has_action)
10796 ExecuteCustomElementAction(x, y, element, page);
10802 static void HandleElementChange(int x, int y, int page)
10804 int element = MovingOrBlocked2Element(x, y);
10805 struct ElementInfo *ei = &element_info[element];
10806 struct ElementChangeInfo *change = &ei->change_page[page];
10809 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
10812 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10813 x, y, element, element_info[element].token_name);
10814 printf("HandleElementChange(): This should never happen!\n");
10819 /* this can happen with classic bombs on walkable, changing elements */
10820 if (!CAN_CHANGE(element))
10823 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
10824 ChangeDelay[x][y] = 0;
10830 if (ChangeDelay[x][y] == 0) /* initialize element change */
10832 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10834 ResetGfxAnimation(x, y);
10835 ResetRandomAnimationValue(x, y);
10837 if (change->pre_change_function)
10838 change->pre_change_function(x, y);
10841 ChangeDelay[x][y]--;
10843 if (ChangeDelay[x][y] != 0) /* continue element change */
10845 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10847 if (IS_ANIMATED(graphic))
10848 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10850 if (change->change_function)
10851 change->change_function(x, y);
10853 else /* finish element change */
10855 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10857 page = ChangePage[x][y];
10858 ChangePage[x][y] = -1;
10860 change = &ei->change_page[page];
10863 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10865 ChangeDelay[x][y] = 1; /* try change after next move step */
10866 ChangePage[x][y] = page; /* remember page to use for change */
10871 if (ChangeElement(x, y, element, page))
10873 if (change->post_change_function)
10874 change->post_change_function(x, y);
10881 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10882 int trigger_element,
10884 int trigger_player,
10888 boolean change_done_any = FALSE;
10889 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10892 if (!(trigger_events[trigger_element][trigger_event]))
10896 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10897 trigger_event, recursion_loop_depth, recursion_loop_detected,
10898 recursion_loop_element, EL_NAME(recursion_loop_element));
10901 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10903 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10905 int element = EL_CUSTOM_START + i;
10906 boolean change_done = FALSE;
10909 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10910 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10913 for (p = 0; p < element_info[element].num_change_pages; p++)
10915 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10917 if (change->can_change_or_has_action &&
10918 change->has_event[trigger_event] &&
10919 change->trigger_side & trigger_side &&
10920 change->trigger_player & trigger_player &&
10921 change->trigger_page & trigger_page_bits &&
10922 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10924 change->actual_trigger_element = trigger_element;
10925 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10926 change->actual_trigger_player_bits = trigger_player;
10927 change->actual_trigger_side = trigger_side;
10928 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10929 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10931 if ((change->can_change && !change_done) || change->has_action)
10935 SCAN_PLAYFIELD(x, y)
10937 if (Feld[x][y] == element)
10939 if (change->can_change && !change_done)
10941 ChangeDelay[x][y] = 1;
10942 ChangeEvent[x][y] = trigger_event;
10944 HandleElementChange(x, y, p);
10946 #if USE_NEW_DELAYED_ACTION
10947 else if (change->has_action)
10949 ExecuteCustomElementAction(x, y, element, p);
10950 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10953 if (change->has_action)
10955 ExecuteCustomElementAction(x, y, element, p);
10956 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10962 if (change->can_change)
10964 change_done = TRUE;
10965 change_done_any = TRUE;
10972 RECURSION_LOOP_DETECTION_END();
10974 return change_done_any;
10977 static boolean CheckElementChangeExt(int x, int y,
10979 int trigger_element,
10981 int trigger_player,
10984 boolean change_done = FALSE;
10987 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10988 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10991 if (Feld[x][y] == EL_BLOCKED)
10993 Blocked2Moving(x, y, &x, &y);
10994 element = Feld[x][y];
10998 /* check if element has already changed */
10999 if (Feld[x][y] != element)
11002 /* check if element has already changed or is about to change after moving */
11003 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11004 Feld[x][y] != element) ||
11006 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11007 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11008 ChangePage[x][y] != -1)))
11013 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11014 trigger_event, recursion_loop_depth, recursion_loop_detected,
11015 recursion_loop_element, EL_NAME(recursion_loop_element));
11018 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11020 for (p = 0; p < element_info[element].num_change_pages; p++)
11022 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11024 /* check trigger element for all events where the element that is checked
11025 for changing interacts with a directly adjacent element -- this is
11026 different to element changes that affect other elements to change on the
11027 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11028 boolean check_trigger_element =
11029 (trigger_event == CE_TOUCHING_X ||
11030 trigger_event == CE_HITTING_X ||
11031 trigger_event == CE_HIT_BY_X ||
11033 /* this one was forgotten until 3.2.3 */
11034 trigger_event == CE_DIGGING_X);
11037 if (change->can_change_or_has_action &&
11038 change->has_event[trigger_event] &&
11039 change->trigger_side & trigger_side &&
11040 change->trigger_player & trigger_player &&
11041 (!check_trigger_element ||
11042 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11044 change->actual_trigger_element = trigger_element;
11045 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11046 change->actual_trigger_player_bits = trigger_player;
11047 change->actual_trigger_side = trigger_side;
11048 change->actual_trigger_ce_value = CustomValue[x][y];
11049 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11051 /* special case: trigger element not at (x,y) position for some events */
11052 if (check_trigger_element)
11064 { 0, 0 }, { 0, 0 }, { 0, 0 },
11068 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11069 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11071 change->actual_trigger_ce_value = CustomValue[xx][yy];
11072 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11075 if (change->can_change && !change_done)
11077 ChangeDelay[x][y] = 1;
11078 ChangeEvent[x][y] = trigger_event;
11080 HandleElementChange(x, y, p);
11082 change_done = TRUE;
11084 #if USE_NEW_DELAYED_ACTION
11085 else if (change->has_action)
11087 ExecuteCustomElementAction(x, y, element, p);
11088 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11091 if (change->has_action)
11093 ExecuteCustomElementAction(x, y, element, p);
11094 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11100 RECURSION_LOOP_DETECTION_END();
11102 return change_done;
11105 static void PlayPlayerSound(struct PlayerInfo *player)
11107 int jx = player->jx, jy = player->jy;
11108 int sound_element = player->artwork_element;
11109 int last_action = player->last_action_waiting;
11110 int action = player->action_waiting;
11112 if (player->is_waiting)
11114 if (action != last_action)
11115 PlayLevelSoundElementAction(jx, jy, sound_element, action);
11117 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11121 if (action != last_action)
11122 StopSound(element_info[sound_element].sound[last_action]);
11124 if (last_action == ACTION_SLEEPING)
11125 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11129 static void PlayAllPlayersSound()
11133 for (i = 0; i < MAX_PLAYERS; i++)
11134 if (stored_player[i].active)
11135 PlayPlayerSound(&stored_player[i]);
11138 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11140 boolean last_waiting = player->is_waiting;
11141 int move_dir = player->MovDir;
11143 player->dir_waiting = move_dir;
11144 player->last_action_waiting = player->action_waiting;
11148 if (!last_waiting) /* not waiting -> waiting */
11150 player->is_waiting = TRUE;
11152 player->frame_counter_bored =
11154 game.player_boring_delay_fixed +
11155 GetSimpleRandom(game.player_boring_delay_random);
11156 player->frame_counter_sleeping =
11158 game.player_sleeping_delay_fixed +
11159 GetSimpleRandom(game.player_sleeping_delay_random);
11161 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11164 if (game.player_sleeping_delay_fixed +
11165 game.player_sleeping_delay_random > 0 &&
11166 player->anim_delay_counter == 0 &&
11167 player->post_delay_counter == 0 &&
11168 FrameCounter >= player->frame_counter_sleeping)
11169 player->is_sleeping = TRUE;
11170 else if (game.player_boring_delay_fixed +
11171 game.player_boring_delay_random > 0 &&
11172 FrameCounter >= player->frame_counter_bored)
11173 player->is_bored = TRUE;
11175 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11176 player->is_bored ? ACTION_BORING :
11179 if (player->is_sleeping && player->use_murphy)
11181 /* special case for sleeping Murphy when leaning against non-free tile */
11183 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11184 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11185 !IS_MOVING(player->jx - 1, player->jy)))
11186 move_dir = MV_LEFT;
11187 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11188 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11189 !IS_MOVING(player->jx + 1, player->jy)))
11190 move_dir = MV_RIGHT;
11192 player->is_sleeping = FALSE;
11194 player->dir_waiting = move_dir;
11197 if (player->is_sleeping)
11199 if (player->num_special_action_sleeping > 0)
11201 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11203 int last_special_action = player->special_action_sleeping;
11204 int num_special_action = player->num_special_action_sleeping;
11205 int special_action =
11206 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11207 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11208 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11209 last_special_action + 1 : ACTION_SLEEPING);
11210 int special_graphic =
11211 el_act_dir2img(player->artwork_element, special_action, move_dir);
11213 player->anim_delay_counter =
11214 graphic_info[special_graphic].anim_delay_fixed +
11215 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11216 player->post_delay_counter =
11217 graphic_info[special_graphic].post_delay_fixed +
11218 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11220 player->special_action_sleeping = special_action;
11223 if (player->anim_delay_counter > 0)
11225 player->action_waiting = player->special_action_sleeping;
11226 player->anim_delay_counter--;
11228 else if (player->post_delay_counter > 0)
11230 player->post_delay_counter--;
11234 else if (player->is_bored)
11236 if (player->num_special_action_bored > 0)
11238 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11240 int special_action =
11241 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11242 int special_graphic =
11243 el_act_dir2img(player->artwork_element, special_action, move_dir);
11245 player->anim_delay_counter =
11246 graphic_info[special_graphic].anim_delay_fixed +
11247 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11248 player->post_delay_counter =
11249 graphic_info[special_graphic].post_delay_fixed +
11250 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11252 player->special_action_bored = special_action;
11255 if (player->anim_delay_counter > 0)
11257 player->action_waiting = player->special_action_bored;
11258 player->anim_delay_counter--;
11260 else if (player->post_delay_counter > 0)
11262 player->post_delay_counter--;
11267 else if (last_waiting) /* waiting -> not waiting */
11269 player->is_waiting = FALSE;
11270 player->is_bored = FALSE;
11271 player->is_sleeping = FALSE;
11273 player->frame_counter_bored = -1;
11274 player->frame_counter_sleeping = -1;
11276 player->anim_delay_counter = 0;
11277 player->post_delay_counter = 0;
11279 player->dir_waiting = player->MovDir;
11280 player->action_waiting = ACTION_DEFAULT;
11282 player->special_action_bored = ACTION_DEFAULT;
11283 player->special_action_sleeping = ACTION_DEFAULT;
11287 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11289 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11290 int left = player_action & JOY_LEFT;
11291 int right = player_action & JOY_RIGHT;
11292 int up = player_action & JOY_UP;
11293 int down = player_action & JOY_DOWN;
11294 int button1 = player_action & JOY_BUTTON_1;
11295 int button2 = player_action & JOY_BUTTON_2;
11296 int dx = (left ? -1 : right ? 1 : 0);
11297 int dy = (up ? -1 : down ? 1 : 0);
11299 if (!player->active || tape.pausing)
11305 snapped = SnapField(player, dx, dy);
11309 dropped = DropElement(player);
11311 moved = MovePlayer(player, dx, dy);
11314 if (tape.single_step && tape.recording && !tape.pausing)
11316 if (button1 || (dropped && !moved))
11318 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11319 SnapField(player, 0, 0); /* stop snapping */
11323 SetPlayerWaiting(player, FALSE);
11325 return player_action;
11329 /* no actions for this player (no input at player's configured device) */
11331 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11332 SnapField(player, 0, 0);
11333 CheckGravityMovementWhenNotMoving(player);
11335 if (player->MovPos == 0)
11336 SetPlayerWaiting(player, TRUE);
11338 if (player->MovPos == 0) /* needed for tape.playing */
11339 player->is_moving = FALSE;
11341 player->is_dropping = FALSE;
11342 player->is_dropping_pressed = FALSE;
11343 player->drop_pressed_delay = 0;
11349 static void CheckLevelTime()
11353 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11355 if (level.native_em_level->lev->home == 0) /* all players at home */
11357 PlayerWins(local_player);
11359 AllPlayersGone = TRUE;
11361 level.native_em_level->lev->home = -1;
11364 if (level.native_em_level->ply[0]->alive == 0 &&
11365 level.native_em_level->ply[1]->alive == 0 &&
11366 level.native_em_level->ply[2]->alive == 0 &&
11367 level.native_em_level->ply[3]->alive == 0) /* all dead */
11368 AllPlayersGone = TRUE;
11371 if (TimeFrames >= FRAMES_PER_SECOND)
11376 for (i = 0; i < MAX_PLAYERS; i++)
11378 struct PlayerInfo *player = &stored_player[i];
11380 if (SHIELD_ON(player))
11382 player->shield_normal_time_left--;
11384 if (player->shield_deadly_time_left > 0)
11385 player->shield_deadly_time_left--;
11389 if (!local_player->LevelSolved && !level.use_step_counter)
11397 if (TimeLeft <= 10 && setup.time_limit)
11398 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11401 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11403 DisplayGameControlValues();
11405 DrawGameValue_Time(TimeLeft);
11408 if (!TimeLeft && setup.time_limit)
11410 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11411 level.native_em_level->lev->killed_out_of_time = TRUE;
11413 for (i = 0; i < MAX_PLAYERS; i++)
11414 KillPlayer(&stored_player[i]);
11418 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11420 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11422 DisplayGameControlValues();
11425 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11426 DrawGameValue_Time(TimePlayed);
11429 level.native_em_level->lev->time =
11430 (level.time == 0 ? TimePlayed : TimeLeft);
11433 if (tape.recording || tape.playing)
11434 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11438 UpdateAndDisplayGameControlValues();
11440 UpdateGameDoorValues();
11441 DrawGameDoorValues();
11445 void AdvanceFrameAndPlayerCounters(int player_nr)
11449 /* advance frame counters (global frame counter and time frame counter) */
11453 /* advance player counters (counters for move delay, move animation etc.) */
11454 for (i = 0; i < MAX_PLAYERS; i++)
11456 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11457 int move_delay_value = stored_player[i].move_delay_value;
11458 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11460 if (!advance_player_counters) /* not all players may be affected */
11463 #if USE_NEW_PLAYER_ANIM
11464 if (move_frames == 0) /* less than one move per game frame */
11466 int stepsize = TILEX / move_delay_value;
11467 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11468 int count = (stored_player[i].is_moving ?
11469 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11471 if (count % delay == 0)
11476 stored_player[i].Frame += move_frames;
11478 if (stored_player[i].MovPos != 0)
11479 stored_player[i].StepFrame += move_frames;
11481 if (stored_player[i].move_delay > 0)
11482 stored_player[i].move_delay--;
11484 /* due to bugs in previous versions, counter must count up, not down */
11485 if (stored_player[i].push_delay != -1)
11486 stored_player[i].push_delay++;
11488 if (stored_player[i].drop_delay > 0)
11489 stored_player[i].drop_delay--;
11491 if (stored_player[i].is_dropping_pressed)
11492 stored_player[i].drop_pressed_delay++;
11496 void StartGameActions(boolean init_network_game, boolean record_tape,
11499 unsigned long new_random_seed = InitRND(random_seed);
11502 TapeStartRecording(new_random_seed);
11504 #if defined(NETWORK_AVALIABLE)
11505 if (init_network_game)
11507 SendToServer_StartPlaying();
11518 static unsigned long game_frame_delay = 0;
11519 unsigned long game_frame_delay_value;
11520 byte *recorded_player_action;
11521 byte summarized_player_action = 0;
11522 byte tape_action[MAX_PLAYERS];
11525 /* detect endless loops, caused by custom element programming */
11526 if (recursion_loop_detected && recursion_loop_depth == 0)
11528 char *message = getStringCat3("Internal Error ! Element ",
11529 EL_NAME(recursion_loop_element),
11530 " caused endless loop ! Quit the game ?");
11532 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11533 EL_NAME(recursion_loop_element));
11535 RequestQuitGameExt(FALSE, level_editor_test_game, message);
11537 recursion_loop_detected = FALSE; /* if game should be continued */
11544 if (game.restart_level)
11545 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
11547 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11549 if (level.native_em_level->lev->home == 0) /* all players at home */
11551 PlayerWins(local_player);
11553 AllPlayersGone = TRUE;
11555 level.native_em_level->lev->home = -1;
11558 if (level.native_em_level->ply[0]->alive == 0 &&
11559 level.native_em_level->ply[1]->alive == 0 &&
11560 level.native_em_level->ply[2]->alive == 0 &&
11561 level.native_em_level->ply[3]->alive == 0) /* all dead */
11562 AllPlayersGone = TRUE;
11565 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11568 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11571 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
11574 game_frame_delay_value =
11575 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11577 if (tape.playing && tape.warp_forward && !tape.pausing)
11578 game_frame_delay_value = 0;
11580 /* ---------- main game synchronization point ---------- */
11582 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11584 if (network_playing && !network_player_action_received)
11586 /* try to get network player actions in time */
11588 #if defined(NETWORK_AVALIABLE)
11589 /* last chance to get network player actions without main loop delay */
11590 HandleNetworking();
11593 /* game was quit by network peer */
11594 if (game_status != GAME_MODE_PLAYING)
11597 if (!network_player_action_received)
11598 return; /* failed to get network player actions in time */
11600 /* do not yet reset "network_player_action_received" (for tape.pausing) */
11606 /* at this point we know that we really continue executing the game */
11608 network_player_action_received = FALSE;
11610 /* when playing tape, read previously recorded player input from tape data */
11611 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11614 /* TapePlayAction() may return NULL when toggling to "pause before death" */
11619 if (tape.set_centered_player)
11621 game.centered_player_nr_next = tape.centered_player_nr_next;
11622 game.set_centered_player = TRUE;
11625 for (i = 0; i < MAX_PLAYERS; i++)
11627 summarized_player_action |= stored_player[i].action;
11629 if (!network_playing)
11630 stored_player[i].effective_action = stored_player[i].action;
11633 #if defined(NETWORK_AVALIABLE)
11634 if (network_playing)
11635 SendToServer_MovePlayer(summarized_player_action);
11638 if (!options.network && !setup.team_mode)
11639 local_player->effective_action = summarized_player_action;
11641 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
11643 for (i = 0; i < MAX_PLAYERS; i++)
11644 stored_player[i].effective_action =
11645 (i == game.centered_player_nr ? summarized_player_action : 0);
11648 if (recorded_player_action != NULL)
11649 for (i = 0; i < MAX_PLAYERS; i++)
11650 stored_player[i].effective_action = recorded_player_action[i];
11652 for (i = 0; i < MAX_PLAYERS; i++)
11654 tape_action[i] = stored_player[i].effective_action;
11656 /* (this can only happen in the R'n'D game engine) */
11657 if (tape.recording && tape_action[i] && !tape.player_participates[i])
11658 tape.player_participates[i] = TRUE; /* player just appeared from CE */
11661 /* only record actions from input devices, but not programmed actions */
11662 if (tape.recording)
11663 TapeRecordAction(tape_action);
11665 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11667 GameActions_EM_Main();
11675 void GameActions_EM_Main()
11677 byte effective_action[MAX_PLAYERS];
11678 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11681 for (i = 0; i < MAX_PLAYERS; i++)
11682 effective_action[i] = stored_player[i].effective_action;
11684 GameActions_EM(effective_action, warp_mode);
11688 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11691 void GameActions_RND()
11693 int magic_wall_x = 0, magic_wall_y = 0;
11694 int i, x, y, element, graphic;
11696 InitPlayfieldScanModeVars();
11698 #if USE_ONE_MORE_CHANGE_PER_FRAME
11699 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11701 SCAN_PLAYFIELD(x, y)
11703 ChangeCount[x][y] = 0;
11704 ChangeEvent[x][y] = -1;
11709 if (game.set_centered_player)
11711 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11713 /* switching to "all players" only possible if all players fit to screen */
11714 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11716 game.centered_player_nr_next = game.centered_player_nr;
11717 game.set_centered_player = FALSE;
11720 /* do not switch focus to non-existing (or non-active) player */
11721 if (game.centered_player_nr_next >= 0 &&
11722 !stored_player[game.centered_player_nr_next].active)
11724 game.centered_player_nr_next = game.centered_player_nr;
11725 game.set_centered_player = FALSE;
11729 if (game.set_centered_player &&
11730 ScreenMovPos == 0) /* screen currently aligned at tile position */
11734 if (game.centered_player_nr_next == -1)
11736 setScreenCenteredToAllPlayers(&sx, &sy);
11740 sx = stored_player[game.centered_player_nr_next].jx;
11741 sy = stored_player[game.centered_player_nr_next].jy;
11744 game.centered_player_nr = game.centered_player_nr_next;
11745 game.set_centered_player = FALSE;
11747 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11748 DrawGameDoorValues();
11751 for (i = 0; i < MAX_PLAYERS; i++)
11753 int actual_player_action = stored_player[i].effective_action;
11756 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11757 - rnd_equinox_tetrachloride 048
11758 - rnd_equinox_tetrachloride_ii 096
11759 - rnd_emanuel_schmieg 002
11760 - doctor_sloan_ww 001, 020
11762 if (stored_player[i].MovPos == 0)
11763 CheckGravityMovement(&stored_player[i]);
11766 /* overwrite programmed action with tape action */
11767 if (stored_player[i].programmed_action)
11768 actual_player_action = stored_player[i].programmed_action;
11770 PlayerActions(&stored_player[i], actual_player_action);
11772 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11775 ScrollScreen(NULL, SCROLL_GO_ON);
11777 /* for backwards compatibility, the following code emulates a fixed bug that
11778 occured when pushing elements (causing elements that just made their last
11779 pushing step to already (if possible) make their first falling step in the
11780 same game frame, which is bad); this code is also needed to use the famous
11781 "spring push bug" which is used in older levels and might be wanted to be
11782 used also in newer levels, but in this case the buggy pushing code is only
11783 affecting the "spring" element and no other elements */
11785 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11787 for (i = 0; i < MAX_PLAYERS; i++)
11789 struct PlayerInfo *player = &stored_player[i];
11790 int x = player->jx;
11791 int y = player->jy;
11793 if (player->active && player->is_pushing && player->is_moving &&
11795 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11796 Feld[x][y] == EL_SPRING))
11798 ContinueMoving(x, y);
11800 /* continue moving after pushing (this is actually a bug) */
11801 if (!IS_MOVING(x, y))
11802 Stop[x][y] = FALSE;
11808 debug_print_timestamp(0, "start main loop profiling");
11811 SCAN_PLAYFIELD(x, y)
11813 ChangeCount[x][y] = 0;
11814 ChangeEvent[x][y] = -1;
11816 /* this must be handled before main playfield loop */
11817 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11820 if (MovDelay[x][y] <= 0)
11824 #if USE_NEW_SNAP_DELAY
11825 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11828 if (MovDelay[x][y] <= 0)
11831 DrawLevelField(x, y);
11833 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11839 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11841 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11842 printf("GameActions(): This should never happen!\n");
11844 ChangePage[x][y] = -1;
11848 Stop[x][y] = FALSE;
11849 if (WasJustMoving[x][y] > 0)
11850 WasJustMoving[x][y]--;
11851 if (WasJustFalling[x][y] > 0)
11852 WasJustFalling[x][y]--;
11853 if (CheckCollision[x][y] > 0)
11854 CheckCollision[x][y]--;
11855 if (CheckImpact[x][y] > 0)
11856 CheckImpact[x][y]--;
11860 /* reset finished pushing action (not done in ContinueMoving() to allow
11861 continuous pushing animation for elements with zero push delay) */
11862 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11864 ResetGfxAnimation(x, y);
11865 DrawLevelField(x, y);
11869 if (IS_BLOCKED(x, y))
11873 Blocked2Moving(x, y, &oldx, &oldy);
11874 if (!IS_MOVING(oldx, oldy))
11876 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11877 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11878 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11879 printf("GameActions(): This should never happen!\n");
11886 debug_print_timestamp(0, "- time for pre-main loop:");
11889 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
11890 SCAN_PLAYFIELD(x, y)
11892 element = Feld[x][y];
11893 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11898 int element2 = element;
11899 int graphic2 = graphic;
11901 int element2 = Feld[x][y];
11902 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
11904 int last_gfx_frame = GfxFrame[x][y];
11906 if (graphic_info[graphic2].anim_global_sync)
11907 GfxFrame[x][y] = FrameCounter;
11908 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
11909 GfxFrame[x][y] = CustomValue[x][y];
11910 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
11911 GfxFrame[x][y] = element_info[element2].collect_score;
11912 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
11913 GfxFrame[x][y] = ChangeDelay[x][y];
11915 if (redraw && GfxFrame[x][y] != last_gfx_frame)
11916 DrawLevelGraphicAnimation(x, y, graphic2);
11919 ResetGfxFrame(x, y, TRUE);
11923 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11924 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11925 ResetRandomAnimationValue(x, y);
11929 SetRandomAnimationValue(x, y);
11933 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11936 #endif // -------------------- !!! TEST ONLY !!! --------------------
11939 debug_print_timestamp(0, "- time for TEST loop: -->");
11942 SCAN_PLAYFIELD(x, y)
11944 element = Feld[x][y];
11945 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11947 ResetGfxFrame(x, y, TRUE);
11949 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11950 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11951 ResetRandomAnimationValue(x, y);
11953 SetRandomAnimationValue(x, y);
11955 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11957 if (IS_INACTIVE(element))
11959 if (IS_ANIMATED(graphic))
11960 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11965 /* this may take place after moving, so 'element' may have changed */
11966 if (IS_CHANGING(x, y) &&
11967 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11969 int page = element_info[element].event_page_nr[CE_DELAY];
11972 HandleElementChange(x, y, page);
11974 if (CAN_CHANGE(element))
11975 HandleElementChange(x, y, page);
11977 if (HAS_ACTION(element))
11978 ExecuteCustomElementAction(x, y, element, page);
11981 element = Feld[x][y];
11982 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11985 #if 0 // ---------------------------------------------------------------------
11987 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11991 element = Feld[x][y];
11992 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11994 if (IS_ANIMATED(graphic) &&
11995 !IS_MOVING(x, y) &&
11997 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11999 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12000 DrawTwinkleOnField(x, y);
12002 else if (IS_MOVING(x, y))
12003 ContinueMoving(x, y);
12010 case EL_EM_EXIT_OPEN:
12011 case EL_SP_EXIT_OPEN:
12012 case EL_STEEL_EXIT_OPEN:
12013 case EL_EM_STEEL_EXIT_OPEN:
12014 case EL_SP_TERMINAL:
12015 case EL_SP_TERMINAL_ACTIVE:
12016 case EL_EXTRA_TIME:
12017 case EL_SHIELD_NORMAL:
12018 case EL_SHIELD_DEADLY:
12019 if (IS_ANIMATED(graphic))
12020 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12023 case EL_DYNAMITE_ACTIVE:
12024 case EL_EM_DYNAMITE_ACTIVE:
12025 case EL_DYNABOMB_PLAYER_1_ACTIVE:
12026 case EL_DYNABOMB_PLAYER_2_ACTIVE:
12027 case EL_DYNABOMB_PLAYER_3_ACTIVE:
12028 case EL_DYNABOMB_PLAYER_4_ACTIVE:
12029 case EL_SP_DISK_RED_ACTIVE:
12030 CheckDynamite(x, y);
12033 case EL_AMOEBA_GROWING:
12034 AmoebeWaechst(x, y);
12037 case EL_AMOEBA_SHRINKING:
12038 AmoebaDisappearing(x, y);
12041 #if !USE_NEW_AMOEBA_CODE
12042 case EL_AMOEBA_WET:
12043 case EL_AMOEBA_DRY:
12044 case EL_AMOEBA_FULL:
12046 case EL_EMC_DRIPPER:
12047 AmoebeAbleger(x, y);
12051 case EL_GAME_OF_LIFE:
12056 case EL_EXIT_CLOSED:
12060 case EL_EM_EXIT_CLOSED:
12064 case EL_STEEL_EXIT_CLOSED:
12065 CheckExitSteel(x, y);
12068 case EL_EM_STEEL_EXIT_CLOSED:
12069 CheckExitSteelEM(x, y);
12072 case EL_SP_EXIT_CLOSED:
12076 case EL_EXPANDABLE_WALL_GROWING:
12077 case EL_EXPANDABLE_STEELWALL_GROWING:
12078 MauerWaechst(x, y);
12081 case EL_EXPANDABLE_WALL:
12082 case EL_EXPANDABLE_WALL_HORIZONTAL:
12083 case EL_EXPANDABLE_WALL_VERTICAL:
12084 case EL_EXPANDABLE_WALL_ANY:
12085 case EL_BD_EXPANDABLE_WALL:
12086 MauerAbleger(x, y);
12089 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12090 case EL_EXPANDABLE_STEELWALL_VERTICAL:
12091 case EL_EXPANDABLE_STEELWALL_ANY:
12092 MauerAblegerStahl(x, y);
12096 CheckForDragon(x, y);
12102 case EL_ELEMENT_SNAPPING:
12103 case EL_DIAGONAL_SHRINKING:
12104 case EL_DIAGONAL_GROWING:
12107 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12109 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12114 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12115 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12120 #else // ---------------------------------------------------------------------
12122 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12126 element = Feld[x][y];
12127 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12129 if (IS_ANIMATED(graphic) &&
12130 !IS_MOVING(x, y) &&
12132 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12134 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12135 DrawTwinkleOnField(x, y);
12137 else if ((element == EL_ACID ||
12138 element == EL_EXIT_OPEN ||
12139 element == EL_EM_EXIT_OPEN ||
12140 element == EL_SP_EXIT_OPEN ||
12141 element == EL_STEEL_EXIT_OPEN ||
12142 element == EL_EM_STEEL_EXIT_OPEN ||
12143 element == EL_SP_TERMINAL ||
12144 element == EL_SP_TERMINAL_ACTIVE ||
12145 element == EL_EXTRA_TIME ||
12146 element == EL_SHIELD_NORMAL ||
12147 element == EL_SHIELD_DEADLY) &&
12148 IS_ANIMATED(graphic))
12149 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12150 else if (IS_MOVING(x, y))
12151 ContinueMoving(x, y);
12152 else if (IS_ACTIVE_BOMB(element))
12153 CheckDynamite(x, y);
12154 else if (element == EL_AMOEBA_GROWING)
12155 AmoebeWaechst(x, y);
12156 else if (element == EL_AMOEBA_SHRINKING)
12157 AmoebaDisappearing(x, y);
12159 #if !USE_NEW_AMOEBA_CODE
12160 else if (IS_AMOEBALIVE(element))
12161 AmoebeAbleger(x, y);
12164 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12166 else if (element == EL_EXIT_CLOSED)
12168 else if (element == EL_EM_EXIT_CLOSED)
12170 else if (element == EL_STEEL_EXIT_CLOSED)
12171 CheckExitSteel(x, y);
12172 else if (element == EL_EM_STEEL_EXIT_CLOSED)
12173 CheckExitSteelEM(x, y);
12174 else if (element == EL_SP_EXIT_CLOSED)
12176 else if (element == EL_EXPANDABLE_WALL_GROWING ||
12177 element == EL_EXPANDABLE_STEELWALL_GROWING)
12178 MauerWaechst(x, y);
12179 else if (element == EL_EXPANDABLE_WALL ||
12180 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12181 element == EL_EXPANDABLE_WALL_VERTICAL ||
12182 element == EL_EXPANDABLE_WALL_ANY ||
12183 element == EL_BD_EXPANDABLE_WALL)
12184 MauerAbleger(x, y);
12185 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12186 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12187 element == EL_EXPANDABLE_STEELWALL_ANY)
12188 MauerAblegerStahl(x, y);
12189 else if (element == EL_FLAMES)
12190 CheckForDragon(x, y);
12191 else if (element == EL_EXPLOSION)
12192 ; /* drawing of correct explosion animation is handled separately */
12193 else if (element == EL_ELEMENT_SNAPPING ||
12194 element == EL_DIAGONAL_SHRINKING ||
12195 element == EL_DIAGONAL_GROWING)
12197 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12199 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12201 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12202 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12204 #endif // ---------------------------------------------------------------------
12206 if (IS_BELT_ACTIVE(element))
12207 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12209 if (game.magic_wall_active)
12211 int jx = local_player->jx, jy = local_player->jy;
12213 /* play the element sound at the position nearest to the player */
12214 if ((element == EL_MAGIC_WALL_FULL ||
12215 element == EL_MAGIC_WALL_ACTIVE ||
12216 element == EL_MAGIC_WALL_EMPTYING ||
12217 element == EL_BD_MAGIC_WALL_FULL ||
12218 element == EL_BD_MAGIC_WALL_ACTIVE ||
12219 element == EL_BD_MAGIC_WALL_EMPTYING ||
12220 element == EL_DC_MAGIC_WALL_FULL ||
12221 element == EL_DC_MAGIC_WALL_ACTIVE ||
12222 element == EL_DC_MAGIC_WALL_EMPTYING) &&
12223 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12232 debug_print_timestamp(0, "- time for MAIN loop: -->");
12235 #if USE_NEW_AMOEBA_CODE
12236 /* new experimental amoeba growth stuff */
12237 if (!(FrameCounter % 8))
12239 static unsigned long random = 1684108901;
12241 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12243 x = RND(lev_fieldx);
12244 y = RND(lev_fieldy);
12245 element = Feld[x][y];
12247 if (!IS_PLAYER(x,y) &&
12248 (element == EL_EMPTY ||
12249 CAN_GROW_INTO(element) ||
12250 element == EL_QUICKSAND_EMPTY ||
12251 element == EL_QUICKSAND_FAST_EMPTY ||
12252 element == EL_ACID_SPLASH_LEFT ||
12253 element == EL_ACID_SPLASH_RIGHT))
12255 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12256 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12257 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12258 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12259 Feld[x][y] = EL_AMOEBA_DROP;
12262 random = random * 129 + 1;
12268 if (game.explosions_delayed)
12271 game.explosions_delayed = FALSE;
12273 SCAN_PLAYFIELD(x, y)
12275 element = Feld[x][y];
12277 if (ExplodeField[x][y])
12278 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12279 else if (element == EL_EXPLOSION)
12280 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12282 ExplodeField[x][y] = EX_TYPE_NONE;
12285 game.explosions_delayed = TRUE;
12288 if (game.magic_wall_active)
12290 if (!(game.magic_wall_time_left % 4))
12292 int element = Feld[magic_wall_x][magic_wall_y];
12294 if (element == EL_BD_MAGIC_WALL_FULL ||
12295 element == EL_BD_MAGIC_WALL_ACTIVE ||
12296 element == EL_BD_MAGIC_WALL_EMPTYING)
12297 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12298 else if (element == EL_DC_MAGIC_WALL_FULL ||
12299 element == EL_DC_MAGIC_WALL_ACTIVE ||
12300 element == EL_DC_MAGIC_WALL_EMPTYING)
12301 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12303 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12306 if (game.magic_wall_time_left > 0)
12308 game.magic_wall_time_left--;
12310 if (!game.magic_wall_time_left)
12312 SCAN_PLAYFIELD(x, y)
12314 element = Feld[x][y];
12316 if (element == EL_MAGIC_WALL_ACTIVE ||
12317 element == EL_MAGIC_WALL_FULL)
12319 Feld[x][y] = EL_MAGIC_WALL_DEAD;
12320 DrawLevelField(x, y);
12322 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12323 element == EL_BD_MAGIC_WALL_FULL)
12325 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12326 DrawLevelField(x, y);
12328 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12329 element == EL_DC_MAGIC_WALL_FULL)
12331 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12332 DrawLevelField(x, y);
12336 game.magic_wall_active = FALSE;
12341 if (game.light_time_left > 0)
12343 game.light_time_left--;
12345 if (game.light_time_left == 0)
12346 RedrawAllLightSwitchesAndInvisibleElements();
12349 if (game.timegate_time_left > 0)
12351 game.timegate_time_left--;
12353 if (game.timegate_time_left == 0)
12354 CloseAllOpenTimegates();
12357 if (game.lenses_time_left > 0)
12359 game.lenses_time_left--;
12361 if (game.lenses_time_left == 0)
12362 RedrawAllInvisibleElementsForLenses();
12365 if (game.magnify_time_left > 0)
12367 game.magnify_time_left--;
12369 if (game.magnify_time_left == 0)
12370 RedrawAllInvisibleElementsForMagnifier();
12373 for (i = 0; i < MAX_PLAYERS; i++)
12375 struct PlayerInfo *player = &stored_player[i];
12377 if (SHIELD_ON(player))
12379 if (player->shield_deadly_time_left)
12380 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12381 else if (player->shield_normal_time_left)
12382 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12389 PlayAllPlayersSound();
12391 if (options.debug) /* calculate frames per second */
12393 static unsigned long fps_counter = 0;
12394 static int fps_frames = 0;
12395 unsigned long fps_delay_ms = Counter() - fps_counter;
12399 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
12401 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12404 fps_counter = Counter();
12407 redraw_mask |= REDRAW_FPS;
12410 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12412 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12414 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12416 local_player->show_envelope = 0;
12420 debug_print_timestamp(0, "stop main loop profiling ");
12421 printf("----------------------------------------------------------\n");
12424 /* use random number generator in every frame to make it less predictable */
12425 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12429 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12431 int min_x = x, min_y = y, max_x = x, max_y = y;
12434 for (i = 0; i < MAX_PLAYERS; i++)
12436 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12438 if (!stored_player[i].active || &stored_player[i] == player)
12441 min_x = MIN(min_x, jx);
12442 min_y = MIN(min_y, jy);
12443 max_x = MAX(max_x, jx);
12444 max_y = MAX(max_y, jy);
12447 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12450 static boolean AllPlayersInVisibleScreen()
12454 for (i = 0; i < MAX_PLAYERS; i++)
12456 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12458 if (!stored_player[i].active)
12461 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12468 void ScrollLevel(int dx, int dy)
12471 static Bitmap *bitmap_db_field2 = NULL;
12472 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12479 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
12480 /* only horizontal XOR vertical scroll direction allowed */
12481 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
12486 if (bitmap_db_field2 == NULL)
12487 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
12489 /* needed when blitting directly to same bitmap -- should not be needed with
12490 recent SDL libraries, but apparently does not work in 1.2.11 directly */
12491 BlitBitmap(drawto_field, bitmap_db_field2,
12492 FX + TILEX * (dx == -1) - softscroll_offset,
12493 FY + TILEY * (dy == -1) - softscroll_offset,
12494 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12495 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12496 FX + TILEX * (dx == 1) - softscroll_offset,
12497 FY + TILEY * (dy == 1) - softscroll_offset);
12498 BlitBitmap(bitmap_db_field2, drawto_field,
12499 FX + TILEX * (dx == 1) - softscroll_offset,
12500 FY + TILEY * (dy == 1) - softscroll_offset,
12501 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12502 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12503 FX + TILEX * (dx == 1) - softscroll_offset,
12504 FY + TILEY * (dy == 1) - softscroll_offset);
12509 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
12510 int xsize = (BX2 - BX1 + 1);
12511 int ysize = (BY2 - BY1 + 1);
12512 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
12513 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
12514 int step = (start < end ? +1 : -1);
12516 for (i = start; i != end; i += step)
12518 BlitBitmap(drawto_field, drawto_field,
12519 FX + TILEX * (dx != 0 ? i + step : 0),
12520 FY + TILEY * (dy != 0 ? i + step : 0),
12521 TILEX * (dx != 0 ? 1 : xsize),
12522 TILEY * (dy != 0 ? 1 : ysize),
12523 FX + TILEX * (dx != 0 ? i : 0),
12524 FY + TILEY * (dy != 0 ? i : 0));
12529 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12531 BlitBitmap(drawto_field, drawto_field,
12532 FX + TILEX * (dx == -1) - softscroll_offset,
12533 FY + TILEY * (dy == -1) - softscroll_offset,
12534 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12535 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12536 FX + TILEX * (dx == 1) - softscroll_offset,
12537 FY + TILEY * (dy == 1) - softscroll_offset);
12543 x = (dx == 1 ? BX1 : BX2);
12544 for (y = BY1; y <= BY2; y++)
12545 DrawScreenField(x, y);
12550 y = (dy == 1 ? BY1 : BY2);
12551 for (x = BX1; x <= BX2; x++)
12552 DrawScreenField(x, y);
12555 redraw_mask |= REDRAW_FIELD;
12558 static boolean canFallDown(struct PlayerInfo *player)
12560 int jx = player->jx, jy = player->jy;
12562 return (IN_LEV_FIELD(jx, jy + 1) &&
12563 (IS_FREE(jx, jy + 1) ||
12564 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12565 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12566 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12569 static boolean canPassField(int x, int y, int move_dir)
12571 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12572 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12573 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12574 int nextx = x + dx;
12575 int nexty = y + dy;
12576 int element = Feld[x][y];
12578 return (IS_PASSABLE_FROM(element, opposite_dir) &&
12579 !CAN_MOVE(element) &&
12580 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12581 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12582 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12585 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12587 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12588 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12589 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12593 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12594 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12595 (IS_DIGGABLE(Feld[newx][newy]) ||
12596 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12597 canPassField(newx, newy, move_dir)));
12600 static void CheckGravityMovement(struct PlayerInfo *player)
12602 #if USE_PLAYER_GRAVITY
12603 if (player->gravity && !player->programmed_action)
12605 if (game.gravity && !player->programmed_action)
12608 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12609 int move_dir_vertical = player->effective_action & MV_VERTICAL;
12610 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12611 int jx = player->jx, jy = player->jy;
12612 boolean player_is_moving_to_valid_field =
12613 (!player_is_snapping &&
12614 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12615 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12616 boolean player_can_fall_down = canFallDown(player);
12618 if (player_can_fall_down &&
12619 !player_is_moving_to_valid_field)
12620 player->programmed_action = MV_DOWN;
12624 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12626 return CheckGravityMovement(player);
12628 #if USE_PLAYER_GRAVITY
12629 if (player->gravity && !player->programmed_action)
12631 if (game.gravity && !player->programmed_action)
12634 int jx = player->jx, jy = player->jy;
12635 boolean field_under_player_is_free =
12636 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12637 boolean player_is_standing_on_valid_field =
12638 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12639 (IS_WALKABLE(Feld[jx][jy]) &&
12640 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12642 if (field_under_player_is_free && !player_is_standing_on_valid_field)
12643 player->programmed_action = MV_DOWN;
12648 MovePlayerOneStep()
12649 -----------------------------------------------------------------------------
12650 dx, dy: direction (non-diagonal) to try to move the player to
12651 real_dx, real_dy: direction as read from input device (can be diagonal)
12654 boolean MovePlayerOneStep(struct PlayerInfo *player,
12655 int dx, int dy, int real_dx, int real_dy)
12657 int jx = player->jx, jy = player->jy;
12658 int new_jx = jx + dx, new_jy = jy + dy;
12659 #if !USE_FIXED_DONT_RUN_INTO
12663 boolean player_can_move = !player->cannot_move;
12665 if (!player->active || (!dx && !dy))
12666 return MP_NO_ACTION;
12668 player->MovDir = (dx < 0 ? MV_LEFT :
12669 dx > 0 ? MV_RIGHT :
12671 dy > 0 ? MV_DOWN : MV_NONE);
12673 if (!IN_LEV_FIELD(new_jx, new_jy))
12674 return MP_NO_ACTION;
12676 if (!player_can_move)
12678 if (player->MovPos == 0)
12680 player->is_moving = FALSE;
12681 player->is_digging = FALSE;
12682 player->is_collecting = FALSE;
12683 player->is_snapping = FALSE;
12684 player->is_pushing = FALSE;
12689 if (!options.network && game.centered_player_nr == -1 &&
12690 !AllPlayersInSight(player, new_jx, new_jy))
12691 return MP_NO_ACTION;
12693 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
12694 return MP_NO_ACTION;
12697 #if !USE_FIXED_DONT_RUN_INTO
12698 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
12700 /* (moved to DigField()) */
12701 if (player_can_move && DONT_RUN_INTO(element))
12703 if (element == EL_ACID && dx == 0 && dy == 1)
12705 SplashAcid(new_jx, new_jy);
12706 Feld[jx][jy] = EL_PLAYER_1;
12707 InitMovingField(jx, jy, MV_DOWN);
12708 Store[jx][jy] = EL_ACID;
12709 ContinueMoving(jx, jy);
12710 BuryPlayer(player);
12713 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12719 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12720 if (can_move != MP_MOVING)
12723 /* check if DigField() has caused relocation of the player */
12724 if (player->jx != jx || player->jy != jy)
12725 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12727 StorePlayer[jx][jy] = 0;
12728 player->last_jx = jx;
12729 player->last_jy = jy;
12730 player->jx = new_jx;
12731 player->jy = new_jy;
12732 StorePlayer[new_jx][new_jy] = player->element_nr;
12734 if (player->move_delay_value_next != -1)
12736 player->move_delay_value = player->move_delay_value_next;
12737 player->move_delay_value_next = -1;
12741 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12743 player->step_counter++;
12745 PlayerVisit[jx][jy] = FrameCounter;
12747 #if USE_UFAST_PLAYER_EXIT_BUGFIX
12748 player->is_moving = TRUE;
12752 /* should better be called in MovePlayer(), but this breaks some tapes */
12753 ScrollPlayer(player, SCROLL_INIT);
12759 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12761 int jx = player->jx, jy = player->jy;
12762 int old_jx = jx, old_jy = jy;
12763 int moved = MP_NO_ACTION;
12765 if (!player->active)
12770 if (player->MovPos == 0)
12772 player->is_moving = FALSE;
12773 player->is_digging = FALSE;
12774 player->is_collecting = FALSE;
12775 player->is_snapping = FALSE;
12776 player->is_pushing = FALSE;
12782 if (player->move_delay > 0)
12785 player->move_delay = -1; /* set to "uninitialized" value */
12787 /* store if player is automatically moved to next field */
12788 player->is_auto_moving = (player->programmed_action != MV_NONE);
12790 /* remove the last programmed player action */
12791 player->programmed_action = 0;
12793 if (player->MovPos)
12795 /* should only happen if pre-1.2 tape recordings are played */
12796 /* this is only for backward compatibility */
12798 int original_move_delay_value = player->move_delay_value;
12801 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
12805 /* scroll remaining steps with finest movement resolution */
12806 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12808 while (player->MovPos)
12810 ScrollPlayer(player, SCROLL_GO_ON);
12811 ScrollScreen(NULL, SCROLL_GO_ON);
12813 AdvanceFrameAndPlayerCounters(player->index_nr);
12819 player->move_delay_value = original_move_delay_value;
12822 player->is_active = FALSE;
12824 if (player->last_move_dir & MV_HORIZONTAL)
12826 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12827 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12831 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12832 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12835 #if USE_FIXED_BORDER_RUNNING_GFX
12836 if (!moved && !player->is_active)
12838 player->is_moving = FALSE;
12839 player->is_digging = FALSE;
12840 player->is_collecting = FALSE;
12841 player->is_snapping = FALSE;
12842 player->is_pushing = FALSE;
12850 if (moved & MP_MOVING && !ScreenMovPos &&
12851 (player->index_nr == game.centered_player_nr ||
12852 game.centered_player_nr == -1))
12854 if (moved & MP_MOVING && !ScreenMovPos &&
12855 (player == local_player || !options.network))
12858 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12859 int offset = game.scroll_delay_value;
12861 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12863 /* actual player has left the screen -- scroll in that direction */
12864 if (jx != old_jx) /* player has moved horizontally */
12865 scroll_x += (jx - old_jx);
12866 else /* player has moved vertically */
12867 scroll_y += (jy - old_jy);
12871 if (jx != old_jx) /* player has moved horizontally */
12873 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
12874 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12875 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12877 /* don't scroll over playfield boundaries */
12878 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12879 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12881 /* don't scroll more than one field at a time */
12882 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12884 /* don't scroll against the player's moving direction */
12885 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
12886 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12887 scroll_x = old_scroll_x;
12889 else /* player has moved vertically */
12891 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
12892 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12893 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12895 /* don't scroll over playfield boundaries */
12896 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12897 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12899 /* don't scroll more than one field at a time */
12900 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12902 /* don't scroll against the player's moving direction */
12903 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
12904 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12905 scroll_y = old_scroll_y;
12909 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12912 if (!options.network && game.centered_player_nr == -1 &&
12913 !AllPlayersInVisibleScreen())
12915 scroll_x = old_scroll_x;
12916 scroll_y = old_scroll_y;
12920 if (!options.network && !AllPlayersInVisibleScreen())
12922 scroll_x = old_scroll_x;
12923 scroll_y = old_scroll_y;
12928 ScrollScreen(player, SCROLL_INIT);
12929 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12934 player->StepFrame = 0;
12936 if (moved & MP_MOVING)
12938 if (old_jx != jx && old_jy == jy)
12939 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12940 else if (old_jx == jx && old_jy != jy)
12941 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12943 DrawLevelField(jx, jy); /* for "crumbled sand" */
12945 player->last_move_dir = player->MovDir;
12946 player->is_moving = TRUE;
12947 player->is_snapping = FALSE;
12948 player->is_switching = FALSE;
12949 player->is_dropping = FALSE;
12950 player->is_dropping_pressed = FALSE;
12951 player->drop_pressed_delay = 0;
12954 /* should better be called here than above, but this breaks some tapes */
12955 ScrollPlayer(player, SCROLL_INIT);
12960 CheckGravityMovementWhenNotMoving(player);
12962 player->is_moving = FALSE;
12964 /* at this point, the player is allowed to move, but cannot move right now
12965 (e.g. because of something blocking the way) -- ensure that the player
12966 is also allowed to move in the next frame (in old versions before 3.1.1,
12967 the player was forced to wait again for eight frames before next try) */
12969 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12970 player->move_delay = 0; /* allow direct movement in the next frame */
12973 if (player->move_delay == -1) /* not yet initialized by DigField() */
12974 player->move_delay = player->move_delay_value;
12976 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12978 TestIfPlayerTouchesBadThing(jx, jy);
12979 TestIfPlayerTouchesCustomElement(jx, jy);
12982 if (!player->active)
12983 RemovePlayer(player);
12988 void ScrollPlayer(struct PlayerInfo *player, int mode)
12990 int jx = player->jx, jy = player->jy;
12991 int last_jx = player->last_jx, last_jy = player->last_jy;
12992 int move_stepsize = TILEX / player->move_delay_value;
12994 #if USE_NEW_PLAYER_SPEED
12995 if (!player->active)
12998 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
13001 if (!player->active || player->MovPos == 0)
13005 if (mode == SCROLL_INIT)
13007 player->actual_frame_counter = FrameCounter;
13008 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13010 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13011 Feld[last_jx][last_jy] == EL_EMPTY)
13013 int last_field_block_delay = 0; /* start with no blocking at all */
13014 int block_delay_adjustment = player->block_delay_adjustment;
13016 /* if player blocks last field, add delay for exactly one move */
13017 if (player->block_last_field)
13019 last_field_block_delay += player->move_delay_value;
13021 /* when blocking enabled, prevent moving up despite gravity */
13022 #if USE_PLAYER_GRAVITY
13023 if (player->gravity && player->MovDir == MV_UP)
13024 block_delay_adjustment = -1;
13026 if (game.gravity && player->MovDir == MV_UP)
13027 block_delay_adjustment = -1;
13031 /* add block delay adjustment (also possible when not blocking) */
13032 last_field_block_delay += block_delay_adjustment;
13034 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13035 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13038 #if USE_NEW_PLAYER_SPEED
13039 if (player->MovPos != 0) /* player has not yet reached destination */
13045 else if (!FrameReached(&player->actual_frame_counter, 1))
13048 #if USE_NEW_PLAYER_SPEED
13049 if (player->MovPos != 0)
13051 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13052 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13054 /* before DrawPlayer() to draw correct player graphic for this case */
13055 if (player->MovPos == 0)
13056 CheckGravityMovement(player);
13059 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13060 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13062 /* before DrawPlayer() to draw correct player graphic for this case */
13063 if (player->MovPos == 0)
13064 CheckGravityMovement(player);
13067 if (player->MovPos == 0) /* player reached destination field */
13069 if (player->move_delay_reset_counter > 0)
13071 player->move_delay_reset_counter--;
13073 if (player->move_delay_reset_counter == 0)
13075 /* continue with normal speed after quickly moving through gate */
13076 HALVE_PLAYER_SPEED(player);
13078 /* be able to make the next move without delay */
13079 player->move_delay = 0;
13083 player->last_jx = jx;
13084 player->last_jy = jy;
13086 if (Feld[jx][jy] == EL_EXIT_OPEN ||
13087 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13088 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13089 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13090 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13091 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
13093 DrawPlayer(player); /* needed here only to cleanup last field */
13094 RemovePlayer(player);
13096 if (local_player->friends_still_needed == 0 ||
13097 IS_SP_ELEMENT(Feld[jx][jy]))
13098 PlayerWins(player);
13101 /* this breaks one level: "machine", level 000 */
13103 int move_direction = player->MovDir;
13104 int enter_side = MV_DIR_OPPOSITE(move_direction);
13105 int leave_side = move_direction;
13106 int old_jx = last_jx;
13107 int old_jy = last_jy;
13108 int old_element = Feld[old_jx][old_jy];
13109 int new_element = Feld[jx][jy];
13111 if (IS_CUSTOM_ELEMENT(old_element))
13112 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13114 player->index_bit, leave_side);
13116 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13117 CE_PLAYER_LEAVES_X,
13118 player->index_bit, leave_side);
13120 if (IS_CUSTOM_ELEMENT(new_element))
13121 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13122 player->index_bit, enter_side);
13124 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13125 CE_PLAYER_ENTERS_X,
13126 player->index_bit, enter_side);
13128 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13129 CE_MOVE_OF_X, move_direction);
13132 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13134 TestIfPlayerTouchesBadThing(jx, jy);
13135 TestIfPlayerTouchesCustomElement(jx, jy);
13137 /* needed because pushed element has not yet reached its destination,
13138 so it would trigger a change event at its previous field location */
13139 if (!player->is_pushing)
13140 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
13142 if (!player->active)
13143 RemovePlayer(player);
13146 if (!local_player->LevelSolved && level.use_step_counter)
13156 if (TimeLeft <= 10 && setup.time_limit)
13157 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13160 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13162 DisplayGameControlValues();
13164 DrawGameValue_Time(TimeLeft);
13167 if (!TimeLeft && setup.time_limit)
13168 for (i = 0; i < MAX_PLAYERS; i++)
13169 KillPlayer(&stored_player[i]);
13172 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13174 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13176 DisplayGameControlValues();
13179 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13180 DrawGameValue_Time(TimePlayed);
13184 if (tape.single_step && tape.recording && !tape.pausing &&
13185 !player->programmed_action)
13186 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13190 void ScrollScreen(struct PlayerInfo *player, int mode)
13192 static unsigned long screen_frame_counter = 0;
13194 if (mode == SCROLL_INIT)
13196 /* set scrolling step size according to actual player's moving speed */
13197 ScrollStepSize = TILEX / player->move_delay_value;
13199 screen_frame_counter = FrameCounter;
13200 ScreenMovDir = player->MovDir;
13201 ScreenMovPos = player->MovPos;
13202 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13205 else if (!FrameReached(&screen_frame_counter, 1))
13210 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13211 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13212 redraw_mask |= REDRAW_FIELD;
13215 ScreenMovDir = MV_NONE;
13218 void TestIfPlayerTouchesCustomElement(int x, int y)
13220 static int xy[4][2] =
13227 static int trigger_sides[4][2] =
13229 /* center side border side */
13230 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13231 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13232 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13233 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13235 static int touch_dir[4] =
13237 MV_LEFT | MV_RIGHT,
13242 int center_element = Feld[x][y]; /* should always be non-moving! */
13245 for (i = 0; i < NUM_DIRECTIONS; i++)
13247 int xx = x + xy[i][0];
13248 int yy = y + xy[i][1];
13249 int center_side = trigger_sides[i][0];
13250 int border_side = trigger_sides[i][1];
13251 int border_element;
13253 if (!IN_LEV_FIELD(xx, yy))
13256 if (IS_PLAYER(x, y))
13258 struct PlayerInfo *player = PLAYERINFO(x, y);
13260 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13261 border_element = Feld[xx][yy]; /* may be moving! */
13262 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13263 border_element = Feld[xx][yy];
13264 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13265 border_element = MovingOrBlocked2Element(xx, yy);
13267 continue; /* center and border element do not touch */
13269 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13270 player->index_bit, border_side);
13271 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13272 CE_PLAYER_TOUCHES_X,
13273 player->index_bit, border_side);
13275 else if (IS_PLAYER(xx, yy))
13277 struct PlayerInfo *player = PLAYERINFO(xx, yy);
13279 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13281 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13282 continue; /* center and border element do not touch */
13285 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13286 player->index_bit, center_side);
13287 CheckTriggeredElementChangeByPlayer(x, y, center_element,
13288 CE_PLAYER_TOUCHES_X,
13289 player->index_bit, center_side);
13295 #if USE_ELEMENT_TOUCHING_BUGFIX
13297 void TestIfElementTouchesCustomElement(int x, int y)
13299 static int xy[4][2] =
13306 static int trigger_sides[4][2] =
13308 /* center side border side */
13309 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13310 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13311 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13312 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13314 static int touch_dir[4] =
13316 MV_LEFT | MV_RIGHT,
13321 boolean change_center_element = FALSE;
13322 int center_element = Feld[x][y]; /* should always be non-moving! */
13323 int border_element_old[NUM_DIRECTIONS];
13326 for (i = 0; i < NUM_DIRECTIONS; i++)
13328 int xx = x + xy[i][0];
13329 int yy = y + xy[i][1];
13330 int border_element;
13332 border_element_old[i] = -1;
13334 if (!IN_LEV_FIELD(xx, yy))
13337 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13338 border_element = Feld[xx][yy]; /* may be moving! */
13339 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13340 border_element = Feld[xx][yy];
13341 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13342 border_element = MovingOrBlocked2Element(xx, yy);
13344 continue; /* center and border element do not touch */
13346 border_element_old[i] = border_element;
13349 for (i = 0; i < NUM_DIRECTIONS; i++)
13351 int xx = x + xy[i][0];
13352 int yy = y + xy[i][1];
13353 int center_side = trigger_sides[i][0];
13354 int border_element = border_element_old[i];
13356 if (border_element == -1)
13359 /* check for change of border element */
13360 CheckElementChangeBySide(xx, yy, border_element, center_element,
13361 CE_TOUCHING_X, center_side);
13364 for (i = 0; i < NUM_DIRECTIONS; i++)
13366 int border_side = trigger_sides[i][1];
13367 int border_element = border_element_old[i];
13369 if (border_element == -1)
13372 /* check for change of center element (but change it only once) */
13373 if (!change_center_element)
13374 change_center_element =
13375 CheckElementChangeBySide(x, y, center_element, border_element,
13376 CE_TOUCHING_X, border_side);
13382 void TestIfElementTouchesCustomElement_OLD(int x, int y)
13384 static int xy[4][2] =
13391 static int trigger_sides[4][2] =
13393 /* center side border side */
13394 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13395 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13396 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13397 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13399 static int touch_dir[4] =
13401 MV_LEFT | MV_RIGHT,
13406 boolean change_center_element = FALSE;
13407 int center_element = Feld[x][y]; /* should always be non-moving! */
13410 for (i = 0; i < NUM_DIRECTIONS; i++)
13412 int xx = x + xy[i][0];
13413 int yy = y + xy[i][1];
13414 int center_side = trigger_sides[i][0];
13415 int border_side = trigger_sides[i][1];
13416 int border_element;
13418 if (!IN_LEV_FIELD(xx, yy))
13421 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13422 border_element = Feld[xx][yy]; /* may be moving! */
13423 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13424 border_element = Feld[xx][yy];
13425 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13426 border_element = MovingOrBlocked2Element(xx, yy);
13428 continue; /* center and border element do not touch */
13430 /* check for change of center element (but change it only once) */
13431 if (!change_center_element)
13432 change_center_element =
13433 CheckElementChangeBySide(x, y, center_element, border_element,
13434 CE_TOUCHING_X, border_side);
13436 /* check for change of border element */
13437 CheckElementChangeBySide(xx, yy, border_element, center_element,
13438 CE_TOUCHING_X, center_side);
13444 void TestIfElementHitsCustomElement(int x, int y, int direction)
13446 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13447 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
13448 int hitx = x + dx, hity = y + dy;
13449 int hitting_element = Feld[x][y];
13450 int touched_element;
13452 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13455 touched_element = (IN_LEV_FIELD(hitx, hity) ?
13456 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13458 if (IN_LEV_FIELD(hitx, hity))
13460 int opposite_direction = MV_DIR_OPPOSITE(direction);
13461 int hitting_side = direction;
13462 int touched_side = opposite_direction;
13463 boolean object_hit = (!IS_MOVING(hitx, hity) ||
13464 MovDir[hitx][hity] != direction ||
13465 ABS(MovPos[hitx][hity]) <= TILEY / 2);
13471 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13472 CE_HITTING_X, touched_side);
13474 CheckElementChangeBySide(hitx, hity, touched_element,
13475 hitting_element, CE_HIT_BY_X, hitting_side);
13477 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13478 CE_HIT_BY_SOMETHING, opposite_direction);
13482 /* "hitting something" is also true when hitting the playfield border */
13483 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13484 CE_HITTING_SOMETHING, direction);
13488 void TestIfElementSmashesCustomElement(int x, int y, int direction)
13490 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13491 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
13492 int hitx = x + dx, hity = y + dy;
13493 int hitting_element = Feld[x][y];
13494 int touched_element;
13496 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
13497 !IS_FREE(hitx, hity) &&
13498 (!IS_MOVING(hitx, hity) ||
13499 MovDir[hitx][hity] != direction ||
13500 ABS(MovPos[hitx][hity]) <= TILEY / 2));
13503 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13507 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
13511 touched_element = (IN_LEV_FIELD(hitx, hity) ?
13512 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13514 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13515 EP_CAN_SMASH_EVERYTHING, direction);
13517 if (IN_LEV_FIELD(hitx, hity))
13519 int opposite_direction = MV_DIR_OPPOSITE(direction);
13520 int hitting_side = direction;
13521 int touched_side = opposite_direction;
13523 int touched_element = MovingOrBlocked2Element(hitx, hity);
13526 boolean object_hit = (!IS_MOVING(hitx, hity) ||
13527 MovDir[hitx][hity] != direction ||
13528 ABS(MovPos[hitx][hity]) <= TILEY / 2);
13537 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13538 CE_SMASHED_BY_SOMETHING, opposite_direction);
13540 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13541 CE_OTHER_IS_SMASHING, touched_side);
13543 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13544 CE_OTHER_GETS_SMASHED, hitting_side);
13550 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13552 int i, kill_x = -1, kill_y = -1;
13554 int bad_element = -1;
13555 static int test_xy[4][2] =
13562 static int test_dir[4] =
13570 for (i = 0; i < NUM_DIRECTIONS; i++)
13572 int test_x, test_y, test_move_dir, test_element;
13574 test_x = good_x + test_xy[i][0];
13575 test_y = good_y + test_xy[i][1];
13577 if (!IN_LEV_FIELD(test_x, test_y))
13581 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13583 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13585 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13586 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13588 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13589 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
13593 bad_element = test_element;
13599 if (kill_x != -1 || kill_y != -1)
13601 if (IS_PLAYER(good_x, good_y))
13603 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13605 if (player->shield_deadly_time_left > 0 &&
13606 !IS_INDESTRUCTIBLE(bad_element))
13607 Bang(kill_x, kill_y);
13608 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13609 KillPlayer(player);
13612 Bang(good_x, good_y);
13616 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13618 int i, kill_x = -1, kill_y = -1;
13619 int bad_element = Feld[bad_x][bad_y];
13620 static int test_xy[4][2] =
13627 static int touch_dir[4] =
13629 MV_LEFT | MV_RIGHT,
13634 static int test_dir[4] =
13642 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
13645 for (i = 0; i < NUM_DIRECTIONS; i++)
13647 int test_x, test_y, test_move_dir, test_element;
13649 test_x = bad_x + test_xy[i][0];
13650 test_y = bad_y + test_xy[i][1];
13651 if (!IN_LEV_FIELD(test_x, test_y))
13655 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13657 test_element = Feld[test_x][test_y];
13659 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13660 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13662 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
13663 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
13665 /* good thing is player or penguin that does not move away */
13666 if (IS_PLAYER(test_x, test_y))
13668 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13670 if (bad_element == EL_ROBOT && player->is_moving)
13671 continue; /* robot does not kill player if he is moving */
13673 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13675 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13676 continue; /* center and border element do not touch */
13683 else if (test_element == EL_PENGUIN)
13692 if (kill_x != -1 || kill_y != -1)
13694 if (IS_PLAYER(kill_x, kill_y))
13696 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13698 if (player->shield_deadly_time_left > 0 &&
13699 !IS_INDESTRUCTIBLE(bad_element))
13700 Bang(bad_x, bad_y);
13701 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13702 KillPlayer(player);
13705 Bang(kill_x, kill_y);
13709 void TestIfPlayerTouchesBadThing(int x, int y)
13711 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13714 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13716 TestIfGoodThingHitsBadThing(x, y, move_dir);
13719 void TestIfBadThingTouchesPlayer(int x, int y)
13721 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13724 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13726 TestIfBadThingHitsGoodThing(x, y, move_dir);
13729 void TestIfFriendTouchesBadThing(int x, int y)
13731 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13734 void TestIfBadThingTouchesFriend(int x, int y)
13736 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13739 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13741 int i, kill_x = bad_x, kill_y = bad_y;
13742 static int xy[4][2] =
13750 for (i = 0; i < NUM_DIRECTIONS; i++)
13754 x = bad_x + xy[i][0];
13755 y = bad_y + xy[i][1];
13756 if (!IN_LEV_FIELD(x, y))
13759 element = Feld[x][y];
13760 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13761 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13769 if (kill_x != bad_x || kill_y != bad_y)
13770 Bang(bad_x, bad_y);
13773 void KillPlayer(struct PlayerInfo *player)
13775 int jx = player->jx, jy = player->jy;
13777 if (!player->active)
13780 /* the following code was introduced to prevent an infinite loop when calling
13782 -> CheckTriggeredElementChangeExt()
13783 -> ExecuteCustomElementAction()
13785 -> (infinitely repeating the above sequence of function calls)
13786 which occurs when killing the player while having a CE with the setting
13787 "kill player X when explosion of <player X>"; the solution using a new
13788 field "player->killed" was chosen for backwards compatibility, although
13789 clever use of the fields "player->active" etc. would probably also work */
13791 if (player->killed)
13795 player->killed = TRUE;
13797 /* remove accessible field at the player's position */
13798 Feld[jx][jy] = EL_EMPTY;
13800 /* deactivate shield (else Bang()/Explode() would not work right) */
13801 player->shield_normal_time_left = 0;
13802 player->shield_deadly_time_left = 0;
13805 BuryPlayer(player);
13808 static void KillPlayerUnlessEnemyProtected(int x, int y)
13810 if (!PLAYER_ENEMY_PROTECTED(x, y))
13811 KillPlayer(PLAYERINFO(x, y));
13814 static void KillPlayerUnlessExplosionProtected(int x, int y)
13816 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13817 KillPlayer(PLAYERINFO(x, y));
13820 void BuryPlayer(struct PlayerInfo *player)
13822 int jx = player->jx, jy = player->jy;
13824 if (!player->active)
13827 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13828 PlayLevelSound(jx, jy, SND_GAME_LOSING);
13830 player->GameOver = TRUE;
13831 RemovePlayer(player);
13834 void RemovePlayer(struct PlayerInfo *player)
13836 int jx = player->jx, jy = player->jy;
13837 int i, found = FALSE;
13839 player->present = FALSE;
13840 player->active = FALSE;
13842 if (!ExplodeField[jx][jy])
13843 StorePlayer[jx][jy] = 0;
13845 if (player->is_moving)
13846 DrawLevelField(player->last_jx, player->last_jy);
13848 for (i = 0; i < MAX_PLAYERS; i++)
13849 if (stored_player[i].active)
13853 AllPlayersGone = TRUE;
13859 #if USE_NEW_SNAP_DELAY
13860 static void setFieldForSnapping(int x, int y, int element, int direction)
13862 struct ElementInfo *ei = &element_info[element];
13863 int direction_bit = MV_DIR_TO_BIT(direction);
13864 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13865 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13866 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13868 Feld[x][y] = EL_ELEMENT_SNAPPING;
13869 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13871 ResetGfxAnimation(x, y);
13873 GfxElement[x][y] = element;
13874 GfxAction[x][y] = action;
13875 GfxDir[x][y] = direction;
13876 GfxFrame[x][y] = -1;
13881 =============================================================================
13882 checkDiagonalPushing()
13883 -----------------------------------------------------------------------------
13884 check if diagonal input device direction results in pushing of object
13885 (by checking if the alternative direction is walkable, diggable, ...)
13886 =============================================================================
13889 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13890 int x, int y, int real_dx, int real_dy)
13892 int jx, jy, dx, dy, xx, yy;
13894 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
13897 /* diagonal direction: check alternative direction */
13902 xx = jx + (dx == 0 ? real_dx : 0);
13903 yy = jy + (dy == 0 ? real_dy : 0);
13905 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13909 =============================================================================
13911 -----------------------------------------------------------------------------
13912 x, y: field next to player (non-diagonal) to try to dig to
13913 real_dx, real_dy: direction as read from input device (can be diagonal)
13914 =============================================================================
13917 int DigField(struct PlayerInfo *player,
13918 int oldx, int oldy, int x, int y,
13919 int real_dx, int real_dy, int mode)
13921 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13922 boolean player_was_pushing = player->is_pushing;
13923 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13924 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13925 int jx = oldx, jy = oldy;
13926 int dx = x - jx, dy = y - jy;
13927 int nextx = x + dx, nexty = y + dy;
13928 int move_direction = (dx == -1 ? MV_LEFT :
13929 dx == +1 ? MV_RIGHT :
13931 dy == +1 ? MV_DOWN : MV_NONE);
13932 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13933 int dig_side = MV_DIR_OPPOSITE(move_direction);
13934 int old_element = Feld[jx][jy];
13935 #if USE_FIXED_DONT_RUN_INTO
13936 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13942 if (is_player) /* function can also be called by EL_PENGUIN */
13944 if (player->MovPos == 0)
13946 player->is_digging = FALSE;
13947 player->is_collecting = FALSE;
13950 if (player->MovPos == 0) /* last pushing move finished */
13951 player->is_pushing = FALSE;
13953 if (mode == DF_NO_PUSH) /* player just stopped pushing */
13955 player->is_switching = FALSE;
13956 player->push_delay = -1;
13958 return MP_NO_ACTION;
13962 #if !USE_FIXED_DONT_RUN_INTO
13963 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13964 return MP_NO_ACTION;
13967 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13968 old_element = Back[jx][jy];
13970 /* in case of element dropped at player position, check background */
13971 else if (Back[jx][jy] != EL_EMPTY &&
13972 game.engine_version >= VERSION_IDENT(2,2,0,0))
13973 old_element = Back[jx][jy];
13975 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13976 return MP_NO_ACTION; /* field has no opening in this direction */
13978 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13979 return MP_NO_ACTION; /* field has no opening in this direction */
13981 #if USE_FIXED_DONT_RUN_INTO
13982 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13986 Feld[jx][jy] = player->artwork_element;
13987 InitMovingField(jx, jy, MV_DOWN);
13988 Store[jx][jy] = EL_ACID;
13989 ContinueMoving(jx, jy);
13990 BuryPlayer(player);
13992 return MP_DONT_RUN_INTO;
13996 #if USE_FIXED_DONT_RUN_INTO
13997 if (player_can_move && DONT_RUN_INTO(element))
13999 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14001 return MP_DONT_RUN_INTO;
14005 #if USE_FIXED_DONT_RUN_INTO
14006 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14007 return MP_NO_ACTION;
14010 #if !USE_FIXED_DONT_RUN_INTO
14011 element = Feld[x][y];
14014 collect_count = element_info[element].collect_count_initial;
14016 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
14017 return MP_NO_ACTION;
14019 if (game.engine_version < VERSION_IDENT(2,2,0,0))
14020 player_can_move = player_can_move_or_snap;
14022 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14023 game.engine_version >= VERSION_IDENT(2,2,0,0))
14025 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14026 player->index_bit, dig_side);
14027 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14028 player->index_bit, dig_side);
14030 if (element == EL_DC_LANDMINE)
14033 if (Feld[x][y] != element) /* field changed by snapping */
14036 return MP_NO_ACTION;
14039 #if USE_PLAYER_GRAVITY
14040 if (player->gravity && is_player && !player->is_auto_moving &&
14041 canFallDown(player) && move_direction != MV_DOWN &&
14042 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14043 return MP_NO_ACTION; /* player cannot walk here due to gravity */
14045 if (game.gravity && is_player && !player->is_auto_moving &&
14046 canFallDown(player) && move_direction != MV_DOWN &&
14047 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14048 return MP_NO_ACTION; /* player cannot walk here due to gravity */
14051 if (player_can_move &&
14052 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14054 int sound_element = SND_ELEMENT(element);
14055 int sound_action = ACTION_WALKING;
14057 if (IS_RND_GATE(element))
14059 if (!player->key[RND_GATE_NR(element)])
14060 return MP_NO_ACTION;
14062 else if (IS_RND_GATE_GRAY(element))
14064 if (!player->key[RND_GATE_GRAY_NR(element)])
14065 return MP_NO_ACTION;
14067 else if (IS_RND_GATE_GRAY_ACTIVE(element))
14069 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14070 return MP_NO_ACTION;
14072 else if (element == EL_EXIT_OPEN ||
14073 element == EL_EM_EXIT_OPEN ||
14074 element == EL_STEEL_EXIT_OPEN ||
14075 element == EL_EM_STEEL_EXIT_OPEN ||
14076 element == EL_SP_EXIT_OPEN ||
14077 element == EL_SP_EXIT_OPENING)
14079 sound_action = ACTION_PASSING; /* player is passing exit */
14081 else if (element == EL_EMPTY)
14083 sound_action = ACTION_MOVING; /* nothing to walk on */
14086 /* play sound from background or player, whatever is available */
14087 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14088 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14090 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14092 else if (player_can_move &&
14093 IS_PASSABLE(element) && canPassField(x, y, move_direction))
14095 if (!ACCESS_FROM(element, opposite_direction))
14096 return MP_NO_ACTION; /* field not accessible from this direction */
14098 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
14099 return MP_NO_ACTION;
14101 if (IS_EM_GATE(element))
14103 if (!player->key[EM_GATE_NR(element)])
14104 return MP_NO_ACTION;
14106 else if (IS_EM_GATE_GRAY(element))
14108 if (!player->key[EM_GATE_GRAY_NR(element)])
14109 return MP_NO_ACTION;
14111 else if (IS_EM_GATE_GRAY_ACTIVE(element))
14113 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14114 return MP_NO_ACTION;
14116 else if (IS_EMC_GATE(element))
14118 if (!player->key[EMC_GATE_NR(element)])
14119 return MP_NO_ACTION;
14121 else if (IS_EMC_GATE_GRAY(element))
14123 if (!player->key[EMC_GATE_GRAY_NR(element)])
14124 return MP_NO_ACTION;
14126 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14128 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14129 return MP_NO_ACTION;
14131 else if (element == EL_DC_GATE_WHITE ||
14132 element == EL_DC_GATE_WHITE_GRAY ||
14133 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14135 if (player->num_white_keys == 0)
14136 return MP_NO_ACTION;
14138 player->num_white_keys--;
14140 else if (IS_SP_PORT(element))
14142 if (element == EL_SP_GRAVITY_PORT_LEFT ||
14143 element == EL_SP_GRAVITY_PORT_RIGHT ||
14144 element == EL_SP_GRAVITY_PORT_UP ||
14145 element == EL_SP_GRAVITY_PORT_DOWN)
14146 #if USE_PLAYER_GRAVITY
14147 player->gravity = !player->gravity;
14149 game.gravity = !game.gravity;
14151 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14152 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14153 element == EL_SP_GRAVITY_ON_PORT_UP ||
14154 element == EL_SP_GRAVITY_ON_PORT_DOWN)
14155 #if USE_PLAYER_GRAVITY
14156 player->gravity = TRUE;
14158 game.gravity = TRUE;
14160 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14161 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14162 element == EL_SP_GRAVITY_OFF_PORT_UP ||
14163 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14164 #if USE_PLAYER_GRAVITY
14165 player->gravity = FALSE;
14167 game.gravity = FALSE;
14171 /* automatically move to the next field with double speed */
14172 player->programmed_action = move_direction;
14174 if (player->move_delay_reset_counter == 0)
14176 player->move_delay_reset_counter = 2; /* two double speed steps */
14178 DOUBLE_PLAYER_SPEED(player);
14181 PlayLevelSoundAction(x, y, ACTION_PASSING);
14183 else if (player_can_move_or_snap && IS_DIGGABLE(element))
14187 if (mode != DF_SNAP)
14189 GfxElement[x][y] = GFX_ELEMENT(element);
14190 player->is_digging = TRUE;
14193 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14195 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14196 player->index_bit, dig_side);
14198 if (mode == DF_SNAP)
14200 #if USE_NEW_SNAP_DELAY
14201 if (level.block_snap_field)
14202 setFieldForSnapping(x, y, element, move_direction);
14204 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14206 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14209 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14210 player->index_bit, dig_side);
14213 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14217 if (is_player && mode != DF_SNAP)
14219 GfxElement[x][y] = element;
14220 player->is_collecting = TRUE;
14223 if (element == EL_SPEED_PILL)
14225 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14227 else if (element == EL_EXTRA_TIME && level.time > 0)
14229 TimeLeft += level.extra_time;
14232 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14234 DisplayGameControlValues();
14236 DrawGameValue_Time(TimeLeft);
14239 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14241 player->shield_normal_time_left += level.shield_normal_time;
14242 if (element == EL_SHIELD_DEADLY)
14243 player->shield_deadly_time_left += level.shield_deadly_time;
14245 else if (element == EL_DYNAMITE ||
14246 element == EL_EM_DYNAMITE ||
14247 element == EL_SP_DISK_RED)
14249 if (player->inventory_size < MAX_INVENTORY_SIZE)
14250 player->inventory_element[player->inventory_size++] = element;
14252 DrawGameDoorValues();
14254 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14256 player->dynabomb_count++;
14257 player->dynabombs_left++;
14259 else if (element == EL_DYNABOMB_INCREASE_SIZE)
14261 player->dynabomb_size++;
14263 else if (element == EL_DYNABOMB_INCREASE_POWER)
14265 player->dynabomb_xl = TRUE;
14267 else if (IS_KEY(element))
14269 player->key[KEY_NR(element)] = TRUE;
14271 DrawGameDoorValues();
14273 else if (element == EL_DC_KEY_WHITE)
14275 player->num_white_keys++;
14277 /* display white keys? */
14278 /* DrawGameDoorValues(); */
14280 else if (IS_ENVELOPE(element))
14282 player->show_envelope = element;
14284 else if (element == EL_EMC_LENSES)
14286 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14288 RedrawAllInvisibleElementsForLenses();
14290 else if (element == EL_EMC_MAGNIFIER)
14292 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14294 RedrawAllInvisibleElementsForMagnifier();
14296 else if (IS_DROPPABLE(element) ||
14297 IS_THROWABLE(element)) /* can be collected and dropped */
14301 if (collect_count == 0)
14302 player->inventory_infinite_element = element;
14304 for (i = 0; i < collect_count; i++)
14305 if (player->inventory_size < MAX_INVENTORY_SIZE)
14306 player->inventory_element[player->inventory_size++] = element;
14308 DrawGameDoorValues();
14310 else if (collect_count > 0)
14312 local_player->gems_still_needed -= collect_count;
14313 if (local_player->gems_still_needed < 0)
14314 local_player->gems_still_needed = 0;
14317 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
14319 DisplayGameControlValues();
14321 DrawGameValue_Emeralds(local_player->gems_still_needed);
14325 RaiseScoreElement(element);
14326 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14329 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14330 player->index_bit, dig_side);
14332 if (mode == DF_SNAP)
14334 #if USE_NEW_SNAP_DELAY
14335 if (level.block_snap_field)
14336 setFieldForSnapping(x, y, element, move_direction);
14338 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14340 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14343 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14344 player->index_bit, dig_side);
14347 else if (player_can_move_or_snap && IS_PUSHABLE(element))
14349 if (mode == DF_SNAP && element != EL_BD_ROCK)
14350 return MP_NO_ACTION;
14352 if (CAN_FALL(element) && dy)
14353 return MP_NO_ACTION;
14355 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14356 !(element == EL_SPRING && level.use_spring_bug))
14357 return MP_NO_ACTION;
14359 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14360 ((move_direction & MV_VERTICAL &&
14361 ((element_info[element].move_pattern & MV_LEFT &&
14362 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14363 (element_info[element].move_pattern & MV_RIGHT &&
14364 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14365 (move_direction & MV_HORIZONTAL &&
14366 ((element_info[element].move_pattern & MV_UP &&
14367 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14368 (element_info[element].move_pattern & MV_DOWN &&
14369 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14370 return MP_NO_ACTION;
14372 /* do not push elements already moving away faster than player */
14373 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14374 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14375 return MP_NO_ACTION;
14377 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14379 if (player->push_delay_value == -1 || !player_was_pushing)
14380 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14382 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14384 if (player->push_delay_value == -1)
14385 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14387 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14389 if (!player->is_pushing)
14390 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14393 player->is_pushing = TRUE;
14394 player->is_active = TRUE;
14396 if (!(IN_LEV_FIELD(nextx, nexty) &&
14397 (IS_FREE(nextx, nexty) ||
14398 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
14399 IS_SB_ELEMENT(element)))))
14400 return MP_NO_ACTION;
14402 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14403 return MP_NO_ACTION;
14405 if (player->push_delay == -1) /* new pushing; restart delay */
14406 player->push_delay = 0;
14408 if (player->push_delay < player->push_delay_value &&
14409 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14410 element != EL_SPRING && element != EL_BALLOON)
14412 /* make sure that there is no move delay before next try to push */
14413 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14414 player->move_delay = 0;
14416 return MP_NO_ACTION;
14419 if (IS_SB_ELEMENT(element))
14421 if (element == EL_SOKOBAN_FIELD_FULL)
14423 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14424 local_player->sokobanfields_still_needed++;
14427 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14429 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14430 local_player->sokobanfields_still_needed--;
14433 Feld[x][y] = EL_SOKOBAN_OBJECT;
14435 if (Back[x][y] == Back[nextx][nexty])
14436 PlayLevelSoundAction(x, y, ACTION_PUSHING);
14437 else if (Back[x][y] != 0)
14438 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14441 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14444 if (local_player->sokobanfields_still_needed == 0 &&
14445 game.emulation == EMU_SOKOBAN)
14447 PlayerWins(player);
14449 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14453 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14455 InitMovingField(x, y, move_direction);
14456 GfxAction[x][y] = ACTION_PUSHING;
14458 if (mode == DF_SNAP)
14459 ContinueMoving(x, y);
14461 MovPos[x][y] = (dx != 0 ? dx : dy);
14463 Pushed[x][y] = TRUE;
14464 Pushed[nextx][nexty] = TRUE;
14466 if (game.engine_version < VERSION_IDENT(2,2,0,7))
14467 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14469 player->push_delay_value = -1; /* get new value later */
14471 /* check for element change _after_ element has been pushed */
14472 if (game.use_change_when_pushing_bug)
14474 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14475 player->index_bit, dig_side);
14476 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14477 player->index_bit, dig_side);
14480 else if (IS_SWITCHABLE(element))
14482 if (PLAYER_SWITCHING(player, x, y))
14484 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14485 player->index_bit, dig_side);
14490 player->is_switching = TRUE;
14491 player->switch_x = x;
14492 player->switch_y = y;
14494 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14496 if (element == EL_ROBOT_WHEEL)
14498 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14502 game.robot_wheel_active = TRUE;
14504 DrawLevelField(x, y);
14506 else if (element == EL_SP_TERMINAL)
14510 SCAN_PLAYFIELD(xx, yy)
14512 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14514 else if (Feld[xx][yy] == EL_SP_TERMINAL)
14515 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14518 else if (IS_BELT_SWITCH(element))
14520 ToggleBeltSwitch(x, y);
14522 else if (element == EL_SWITCHGATE_SWITCH_UP ||
14523 element == EL_SWITCHGATE_SWITCH_DOWN ||
14524 element == EL_DC_SWITCHGATE_SWITCH_UP ||
14525 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14527 ToggleSwitchgateSwitch(x, y);
14529 else if (element == EL_LIGHT_SWITCH ||
14530 element == EL_LIGHT_SWITCH_ACTIVE)
14532 ToggleLightSwitch(x, y);
14534 else if (element == EL_TIMEGATE_SWITCH ||
14535 element == EL_DC_TIMEGATE_SWITCH)
14537 ActivateTimegateSwitch(x, y);
14539 else if (element == EL_BALLOON_SWITCH_LEFT ||
14540 element == EL_BALLOON_SWITCH_RIGHT ||
14541 element == EL_BALLOON_SWITCH_UP ||
14542 element == EL_BALLOON_SWITCH_DOWN ||
14543 element == EL_BALLOON_SWITCH_NONE ||
14544 element == EL_BALLOON_SWITCH_ANY)
14546 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
14547 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14548 element == EL_BALLOON_SWITCH_UP ? MV_UP :
14549 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
14550 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
14553 else if (element == EL_LAMP)
14555 Feld[x][y] = EL_LAMP_ACTIVE;
14556 local_player->lights_still_needed--;
14558 ResetGfxAnimation(x, y);
14559 DrawLevelField(x, y);
14561 else if (element == EL_TIME_ORB_FULL)
14563 Feld[x][y] = EL_TIME_ORB_EMPTY;
14565 if (level.time > 0 || level.use_time_orb_bug)
14567 TimeLeft += level.time_orb_time;
14570 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14572 DisplayGameControlValues();
14574 DrawGameValue_Time(TimeLeft);
14578 ResetGfxAnimation(x, y);
14579 DrawLevelField(x, y);
14581 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14582 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14586 game.ball_state = !game.ball_state;
14588 SCAN_PLAYFIELD(xx, yy)
14590 int e = Feld[xx][yy];
14592 if (game.ball_state)
14594 if (e == EL_EMC_MAGIC_BALL)
14595 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14596 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14597 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14601 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14602 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14603 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14604 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14609 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14610 player->index_bit, dig_side);
14612 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14613 player->index_bit, dig_side);
14615 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14616 player->index_bit, dig_side);
14622 if (!PLAYER_SWITCHING(player, x, y))
14624 player->is_switching = TRUE;
14625 player->switch_x = x;
14626 player->switch_y = y;
14628 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14629 player->index_bit, dig_side);
14630 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14631 player->index_bit, dig_side);
14633 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14634 player->index_bit, dig_side);
14635 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14636 player->index_bit, dig_side);
14639 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14640 player->index_bit, dig_side);
14641 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14642 player->index_bit, dig_side);
14644 return MP_NO_ACTION;
14647 player->push_delay = -1;
14649 if (is_player) /* function can also be called by EL_PENGUIN */
14651 if (Feld[x][y] != element) /* really digged/collected something */
14653 player->is_collecting = !player->is_digging;
14654 player->is_active = TRUE;
14661 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14663 int jx = player->jx, jy = player->jy;
14664 int x = jx + dx, y = jy + dy;
14665 int snap_direction = (dx == -1 ? MV_LEFT :
14666 dx == +1 ? MV_RIGHT :
14668 dy == +1 ? MV_DOWN : MV_NONE);
14669 boolean can_continue_snapping = (level.continuous_snapping &&
14670 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14672 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14675 if (!player->active || !IN_LEV_FIELD(x, y))
14683 if (player->MovPos == 0)
14684 player->is_pushing = FALSE;
14686 player->is_snapping = FALSE;
14688 if (player->MovPos == 0)
14690 player->is_moving = FALSE;
14691 player->is_digging = FALSE;
14692 player->is_collecting = FALSE;
14698 #if USE_NEW_CONTINUOUS_SNAPPING
14699 /* prevent snapping with already pressed snap key when not allowed */
14700 if (player->is_snapping && !can_continue_snapping)
14703 if (player->is_snapping)
14707 player->MovDir = snap_direction;
14709 if (player->MovPos == 0)
14711 player->is_moving = FALSE;
14712 player->is_digging = FALSE;
14713 player->is_collecting = FALSE;
14716 player->is_dropping = FALSE;
14717 player->is_dropping_pressed = FALSE;
14718 player->drop_pressed_delay = 0;
14720 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14723 player->is_snapping = TRUE;
14724 player->is_active = TRUE;
14726 if (player->MovPos == 0)
14728 player->is_moving = FALSE;
14729 player->is_digging = FALSE;
14730 player->is_collecting = FALSE;
14733 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
14734 DrawLevelField(player->last_jx, player->last_jy);
14736 DrawLevelField(x, y);
14741 boolean DropElement(struct PlayerInfo *player)
14743 int old_element, new_element;
14744 int dropx = player->jx, dropy = player->jy;
14745 int drop_direction = player->MovDir;
14746 int drop_side = drop_direction;
14748 int drop_element = get_next_dropped_element(player);
14750 int drop_element = (player->inventory_size > 0 ?
14751 player->inventory_element[player->inventory_size - 1] :
14752 player->inventory_infinite_element != EL_UNDEFINED ?
14753 player->inventory_infinite_element :
14754 player->dynabombs_left > 0 ?
14755 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
14759 player->is_dropping_pressed = TRUE;
14761 /* do not drop an element on top of another element; when holding drop key
14762 pressed without moving, dropped element must move away before the next
14763 element can be dropped (this is especially important if the next element
14764 is dynamite, which can be placed on background for historical reasons) */
14765 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14768 if (IS_THROWABLE(drop_element))
14770 dropx += GET_DX_FROM_DIR(drop_direction);
14771 dropy += GET_DY_FROM_DIR(drop_direction);
14773 if (!IN_LEV_FIELD(dropx, dropy))
14777 old_element = Feld[dropx][dropy]; /* old element at dropping position */
14778 new_element = drop_element; /* default: no change when dropping */
14780 /* check if player is active, not moving and ready to drop */
14781 if (!player->active || player->MovPos || player->drop_delay > 0)
14784 /* check if player has anything that can be dropped */
14785 if (new_element == EL_UNDEFINED)
14788 /* check if drop key was pressed long enough for EM style dynamite */
14789 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14792 /* check if anything can be dropped at the current position */
14793 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14796 /* collected custom elements can only be dropped on empty fields */
14797 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14800 if (old_element != EL_EMPTY)
14801 Back[dropx][dropy] = old_element; /* store old element on this field */
14803 ResetGfxAnimation(dropx, dropy);
14804 ResetRandomAnimationValue(dropx, dropy);
14806 if (player->inventory_size > 0 ||
14807 player->inventory_infinite_element != EL_UNDEFINED)
14809 if (player->inventory_size > 0)
14811 player->inventory_size--;
14813 DrawGameDoorValues();
14815 if (new_element == EL_DYNAMITE)
14816 new_element = EL_DYNAMITE_ACTIVE;
14817 else if (new_element == EL_EM_DYNAMITE)
14818 new_element = EL_EM_DYNAMITE_ACTIVE;
14819 else if (new_element == EL_SP_DISK_RED)
14820 new_element = EL_SP_DISK_RED_ACTIVE;
14823 Feld[dropx][dropy] = new_element;
14825 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14826 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14827 el2img(Feld[dropx][dropy]), 0);
14829 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14831 /* needed if previous element just changed to "empty" in the last frame */
14832 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14834 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14835 player->index_bit, drop_side);
14836 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14838 player->index_bit, drop_side);
14840 TestIfElementTouchesCustomElement(dropx, dropy);
14842 else /* player is dropping a dyna bomb */
14844 player->dynabombs_left--;
14846 Feld[dropx][dropy] = new_element;
14848 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14849 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14850 el2img(Feld[dropx][dropy]), 0);
14852 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14855 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14856 InitField_WithBug1(dropx, dropy, FALSE);
14858 new_element = Feld[dropx][dropy]; /* element might have changed */
14860 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14861 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14863 int move_direction, nextx, nexty;
14865 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14866 MovDir[dropx][dropy] = drop_direction;
14868 move_direction = MovDir[dropx][dropy];
14869 nextx = dropx + GET_DX_FROM_DIR(move_direction);
14870 nexty = dropy + GET_DY_FROM_DIR(move_direction);
14872 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14874 #if USE_FIX_IMPACT_COLLISION
14875 /* do not cause impact style collision by dropping elements that can fall */
14876 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14878 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14882 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14883 player->is_dropping = TRUE;
14885 player->drop_pressed_delay = 0;
14886 player->is_dropping_pressed = FALSE;
14888 player->drop_x = dropx;
14889 player->drop_y = dropy;
14894 /* ------------------------------------------------------------------------- */
14895 /* game sound playing functions */
14896 /* ------------------------------------------------------------------------- */
14898 static int *loop_sound_frame = NULL;
14899 static int *loop_sound_volume = NULL;
14901 void InitPlayLevelSound()
14903 int num_sounds = getSoundListSize();
14905 checked_free(loop_sound_frame);
14906 checked_free(loop_sound_volume);
14908 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
14909 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14912 static void PlayLevelSound(int x, int y, int nr)
14914 int sx = SCREENX(x), sy = SCREENY(y);
14915 int volume, stereo_position;
14916 int max_distance = 8;
14917 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14919 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14920 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14923 if (!IN_LEV_FIELD(x, y) ||
14924 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14925 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14928 volume = SOUND_MAX_VOLUME;
14930 if (!IN_SCR_FIELD(sx, sy))
14932 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14933 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14935 volume -= volume * (dx > dy ? dx : dy) / max_distance;
14938 stereo_position = (SOUND_MAX_LEFT +
14939 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14940 (SCR_FIELDX + 2 * max_distance));
14942 if (IS_LOOP_SOUND(nr))
14944 /* This assures that quieter loop sounds do not overwrite louder ones,
14945 while restarting sound volume comparison with each new game frame. */
14947 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14950 loop_sound_volume[nr] = volume;
14951 loop_sound_frame[nr] = FrameCounter;
14954 PlaySoundExt(nr, volume, stereo_position, type);
14957 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14959 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14960 x > LEVELX(BX2) ? LEVELX(BX2) : x,
14961 y < LEVELY(BY1) ? LEVELY(BY1) :
14962 y > LEVELY(BY2) ? LEVELY(BY2) : y,
14966 static void PlayLevelSoundAction(int x, int y, int action)
14968 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14971 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14973 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14975 if (sound_effect != SND_UNDEFINED)
14976 PlayLevelSound(x, y, sound_effect);
14979 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14982 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14984 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14985 PlayLevelSound(x, y, sound_effect);
14988 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14990 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14992 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14993 PlayLevelSound(x, y, sound_effect);
14996 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14998 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15000 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15001 StopSound(sound_effect);
15004 static void PlayLevelMusic()
15006 if (levelset.music[level_nr] != MUS_UNDEFINED)
15007 PlayMusic(levelset.music[level_nr]); /* from config file */
15009 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
15012 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15014 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
15015 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
15016 int x = xx - 1 - offset;
15017 int y = yy - 1 - offset;
15022 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15026 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15030 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15034 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15038 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15042 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15046 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15049 case SAMPLE_android_clone:
15050 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15053 case SAMPLE_android_move:
15054 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15057 case SAMPLE_spring:
15058 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15062 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15066 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15069 case SAMPLE_eater_eat:
15070 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15074 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15077 case SAMPLE_collect:
15078 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15081 case SAMPLE_diamond:
15082 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15085 case SAMPLE_squash:
15086 /* !!! CHECK THIS !!! */
15088 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15090 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15094 case SAMPLE_wonderfall:
15095 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15099 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15103 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15107 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15111 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15115 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15119 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15122 case SAMPLE_wonder:
15123 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15127 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15130 case SAMPLE_exit_open:
15131 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15134 case SAMPLE_exit_leave:
15135 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15138 case SAMPLE_dynamite:
15139 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15143 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15147 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15151 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15155 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15159 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15163 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15167 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15173 void ChangeTime(int value)
15175 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
15179 /* EMC game engine uses value from time counter of RND game engine */
15180 level.native_em_level->lev->time = *time;
15182 DrawGameValue_Time(*time);
15185 void RaiseScore(int value)
15187 /* EMC game engine and RND game engine have separate score counters */
15188 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
15189 &level.native_em_level->lev->score : &local_player->score);
15193 DrawGameValue_Score(*score);
15197 void RaiseScore(int value)
15199 local_player->score += value;
15202 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
15204 DisplayGameControlValues();
15206 DrawGameValue_Score(local_player->score);
15210 void RaiseScoreElement(int element)
15215 case EL_BD_DIAMOND:
15216 case EL_EMERALD_YELLOW:
15217 case EL_EMERALD_RED:
15218 case EL_EMERALD_PURPLE:
15219 case EL_SP_INFOTRON:
15220 RaiseScore(level.score[SC_EMERALD]);
15223 RaiseScore(level.score[SC_DIAMOND]);
15226 RaiseScore(level.score[SC_CRYSTAL]);
15229 RaiseScore(level.score[SC_PEARL]);
15232 case EL_BD_BUTTERFLY:
15233 case EL_SP_ELECTRON:
15234 RaiseScore(level.score[SC_BUG]);
15237 case EL_BD_FIREFLY:
15238 case EL_SP_SNIKSNAK:
15239 RaiseScore(level.score[SC_SPACESHIP]);
15242 case EL_DARK_YAMYAM:
15243 RaiseScore(level.score[SC_YAMYAM]);
15246 RaiseScore(level.score[SC_ROBOT]);
15249 RaiseScore(level.score[SC_PACMAN]);
15252 RaiseScore(level.score[SC_NUT]);
15255 case EL_EM_DYNAMITE:
15256 case EL_SP_DISK_RED:
15257 case EL_DYNABOMB_INCREASE_NUMBER:
15258 case EL_DYNABOMB_INCREASE_SIZE:
15259 case EL_DYNABOMB_INCREASE_POWER:
15260 RaiseScore(level.score[SC_DYNAMITE]);
15262 case EL_SHIELD_NORMAL:
15263 case EL_SHIELD_DEADLY:
15264 RaiseScore(level.score[SC_SHIELD]);
15266 case EL_EXTRA_TIME:
15267 RaiseScore(level.extra_time_score);
15281 case EL_DC_KEY_WHITE:
15282 RaiseScore(level.score[SC_KEY]);
15285 RaiseScore(element_info[element].collect_score);
15290 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15292 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15294 #if defined(NETWORK_AVALIABLE)
15295 if (options.network)
15296 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15305 FadeSkipNextFadeIn();
15307 fading = fading_none;
15311 OpenDoor(DOOR_CLOSE_1);
15314 game_status = GAME_MODE_MAIN;
15317 DrawAndFadeInMainMenu(REDRAW_FIELD);
15325 FadeOut(REDRAW_FIELD);
15328 game_status = GAME_MODE_MAIN;
15330 DrawAndFadeInMainMenu(REDRAW_FIELD);
15334 else /* continue playing the game */
15336 if (tape.playing && tape.deactivate_display)
15337 TapeDeactivateDisplayOff(TRUE);
15339 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15341 if (tape.playing && tape.deactivate_display)
15342 TapeDeactivateDisplayOn();
15346 void RequestQuitGame(boolean ask_if_really_quit)
15348 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15349 boolean skip_request = AllPlayersGone || quick_quit;
15351 RequestQuitGameExt(skip_request, quick_quit,
15352 "Do you really want to quit the game ?");
15356 /* ------------------------------------------------------------------------- */
15357 /* random generator functions */
15358 /* ------------------------------------------------------------------------- */
15360 unsigned int InitEngineRandom_RND(long seed)
15362 game.num_random_calls = 0;
15365 unsigned int rnd_seed = InitEngineRandom(seed);
15367 printf("::: START RND: %d\n", rnd_seed);
15372 return InitEngineRandom(seed);
15378 unsigned int RND(int max)
15382 game.num_random_calls++;
15384 return GetEngineRandom(max);
15391 /* ------------------------------------------------------------------------- */
15392 /* game engine snapshot handling functions */
15393 /* ------------------------------------------------------------------------- */
15395 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
15397 struct EngineSnapshotInfo
15399 /* runtime values for custom element collect score */
15400 int collect_score[NUM_CUSTOM_ELEMENTS];
15402 /* runtime values for group element choice position */
15403 int choice_pos[NUM_GROUP_ELEMENTS];
15405 /* runtime values for belt position animations */
15406 int belt_graphic[4 * NUM_BELT_PARTS];
15407 int belt_anim_mode[4 * NUM_BELT_PARTS];
15410 struct EngineSnapshotNodeInfo
15417 static struct EngineSnapshotInfo engine_snapshot_rnd;
15418 static ListNode *engine_snapshot_list = NULL;
15419 static char *snapshot_level_identifier = NULL;
15420 static int snapshot_level_nr = -1;
15422 void FreeEngineSnapshot()
15424 while (engine_snapshot_list != NULL)
15425 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
15428 setString(&snapshot_level_identifier, NULL);
15429 snapshot_level_nr = -1;
15432 static void SaveEngineSnapshotValues_RND()
15434 static int belt_base_active_element[4] =
15436 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15437 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15438 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15439 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15443 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15445 int element = EL_CUSTOM_START + i;
15447 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15450 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15452 int element = EL_GROUP_START + i;
15454 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15457 for (i = 0; i < 4; i++)
15459 for (j = 0; j < NUM_BELT_PARTS; j++)
15461 int element = belt_base_active_element[i] + j;
15462 int graphic = el2img(element);
15463 int anim_mode = graphic_info[graphic].anim_mode;
15465 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
15466 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
15471 static void LoadEngineSnapshotValues_RND()
15473 unsigned long num_random_calls = game.num_random_calls;
15476 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15478 int element = EL_CUSTOM_START + i;
15480 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15483 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15485 int element = EL_GROUP_START + i;
15487 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15490 for (i = 0; i < 4; i++)
15492 for (j = 0; j < NUM_BELT_PARTS; j++)
15494 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
15495 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
15497 graphic_info[graphic].anim_mode = anim_mode;
15501 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15503 InitRND(tape.random_seed);
15504 for (i = 0; i < num_random_calls; i++)
15508 if (game.num_random_calls != num_random_calls)
15510 Error(ERR_INFO, "number of random calls out of sync");
15511 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15512 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15513 Error(ERR_EXIT, "this should not happen -- please debug");
15517 static void SaveEngineSnapshotBuffer(void *buffer, int size)
15519 struct EngineSnapshotNodeInfo *bi =
15520 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
15522 bi->buffer_orig = buffer;
15523 bi->buffer_copy = checked_malloc(size);
15526 memcpy(bi->buffer_copy, buffer, size);
15528 addNodeToList(&engine_snapshot_list, NULL, bi);
15531 void SaveEngineSnapshot()
15533 FreeEngineSnapshot(); /* free previous snapshot, if needed */
15535 if (level_editor_test_game) /* do not save snapshots from editor */
15538 /* copy some special values to a structure better suited for the snapshot */
15540 SaveEngineSnapshotValues_RND();
15541 SaveEngineSnapshotValues_EM();
15543 /* save values stored in special snapshot structure */
15545 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15546 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15548 /* save further RND engine values */
15550 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
15551 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
15552 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
15554 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
15555 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
15556 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
15557 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
15559 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15560 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15561 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15562 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15563 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15565 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15566 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15567 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15569 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15571 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15573 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15574 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15576 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
15577 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
15578 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
15579 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15580 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15581 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15582 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15583 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
15584 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
15585 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15586 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
15587 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15588 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15589 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15590 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15591 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15592 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
15593 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
15595 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15596 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15598 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15599 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15600 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15602 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15603 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15605 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15606 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15607 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15608 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15609 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15611 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15612 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15614 /* save level identification information */
15616 setString(&snapshot_level_identifier, leveldir_current->identifier);
15617 snapshot_level_nr = level_nr;
15620 ListNode *node = engine_snapshot_list;
15623 while (node != NULL)
15625 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15630 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15634 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
15636 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
15639 void LoadEngineSnapshot()
15641 ListNode *node = engine_snapshot_list;
15643 if (engine_snapshot_list == NULL)
15646 while (node != NULL)
15648 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
15653 /* restore special values from snapshot structure */
15655 LoadEngineSnapshotValues_RND();
15656 LoadEngineSnapshotValues_EM();
15659 boolean CheckEngineSnapshot()
15661 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15662 snapshot_level_nr == level_nr);
15666 /* ---------- new game button stuff ---------------------------------------- */
15668 /* graphic position values for game buttons */
15669 #define GAME_BUTTON_XSIZE 30
15670 #define GAME_BUTTON_YSIZE 30
15671 #define GAME_BUTTON_XPOS 5
15672 #define GAME_BUTTON_YPOS 215
15673 #define SOUND_BUTTON_XPOS 5
15674 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
15676 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15677 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15678 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15679 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15680 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15681 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15689 } gamebutton_info[NUM_GAME_BUTTONS] =
15693 &game.button.stop.x, &game.button.stop.y,
15694 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
15699 &game.button.pause.x, &game.button.pause.y,
15700 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
15701 GAME_CTRL_ID_PAUSE,
15705 &game.button.play.x, &game.button.play.y,
15706 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
15711 &game.button.sound_music.x, &game.button.sound_music.y,
15712 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
15713 SOUND_CTRL_ID_MUSIC,
15714 "background music on/off"
15717 &game.button.sound_loops.x, &game.button.sound_loops.y,
15718 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
15719 SOUND_CTRL_ID_LOOPS,
15720 "sound loops on/off"
15723 &game.button.sound_simple.x,&game.button.sound_simple.y,
15724 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
15725 SOUND_CTRL_ID_SIMPLE,
15726 "normal sounds on/off"
15730 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
15735 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
15736 GAME_CTRL_ID_PAUSE,
15740 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
15745 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
15746 SOUND_CTRL_ID_MUSIC,
15747 "background music on/off"
15750 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
15751 SOUND_CTRL_ID_LOOPS,
15752 "sound loops on/off"
15755 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
15756 SOUND_CTRL_ID_SIMPLE,
15757 "normal sounds on/off"
15762 void CreateGameButtons()
15766 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15768 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
15769 struct GadgetInfo *gi;
15772 unsigned long event_mask;
15774 int gd_xoffset, gd_yoffset;
15775 int gd_x1, gd_x2, gd_y1, gd_y2;
15778 x = DX + *gamebutton_info[i].x;
15779 y = DY + *gamebutton_info[i].y;
15780 gd_xoffset = gamebutton_info[i].gd_x;
15781 gd_yoffset = gamebutton_info[i].gd_y;
15782 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
15783 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
15785 if (id == GAME_CTRL_ID_STOP ||
15786 id == GAME_CTRL_ID_PAUSE ||
15787 id == GAME_CTRL_ID_PLAY)
15789 button_type = GD_TYPE_NORMAL_BUTTON;
15791 event_mask = GD_EVENT_RELEASED;
15792 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15793 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15797 button_type = GD_TYPE_CHECK_BUTTON;
15799 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15800 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15801 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15802 event_mask = GD_EVENT_PRESSED;
15803 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
15804 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15807 gi = CreateGadget(GDI_CUSTOM_ID, id,
15808 GDI_INFO_TEXT, gamebutton_info[i].infotext,
15813 GDI_X, DX + gd_xoffset,
15814 GDI_Y, DY + gd_yoffset,
15816 GDI_WIDTH, GAME_BUTTON_XSIZE,
15817 GDI_HEIGHT, GAME_BUTTON_YSIZE,
15818 GDI_TYPE, button_type,
15819 GDI_STATE, GD_BUTTON_UNPRESSED,
15820 GDI_CHECKED, checked,
15821 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
15822 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
15823 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
15824 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
15825 GDI_DIRECT_DRAW, FALSE,
15826 GDI_EVENT_MASK, event_mask,
15827 GDI_CALLBACK_ACTION, HandleGameButtons,
15831 Error(ERR_EXIT, "cannot create gadget");
15833 game_gadget[id] = gi;
15837 void FreeGameButtons()
15841 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15842 FreeGadget(game_gadget[i]);
15845 static void MapGameButtons()
15849 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15850 MapGadget(game_gadget[i]);
15853 void UnmapGameButtons()
15857 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15858 UnmapGadget(game_gadget[i]);
15861 void RedrawGameButtons()
15865 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15866 RedrawGadget(game_gadget[i]);
15869 static void HandleGameButtons(struct GadgetInfo *gi)
15871 int id = gi->custom_id;
15873 if (game_status != GAME_MODE_PLAYING)
15878 case GAME_CTRL_ID_STOP:
15882 RequestQuitGame(TRUE);
15885 case GAME_CTRL_ID_PAUSE:
15886 if (options.network)
15888 #if defined(NETWORK_AVALIABLE)
15890 SendToServer_ContinuePlaying();
15892 SendToServer_PausePlaying();
15896 TapeTogglePause(TAPE_TOGGLE_MANUAL);
15899 case GAME_CTRL_ID_PLAY:
15902 #if defined(NETWORK_AVALIABLE)
15903 if (options.network)
15904 SendToServer_ContinuePlaying();
15908 tape.pausing = FALSE;
15909 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
15914 case SOUND_CTRL_ID_MUSIC:
15915 if (setup.sound_music)
15917 setup.sound_music = FALSE;
15920 else if (audio.music_available)
15922 setup.sound = setup.sound_music = TRUE;
15924 SetAudioMode(setup.sound);
15930 case SOUND_CTRL_ID_LOOPS:
15931 if (setup.sound_loops)
15932 setup.sound_loops = FALSE;
15933 else if (audio.loops_available)
15935 setup.sound = setup.sound_loops = TRUE;
15936 SetAudioMode(setup.sound);
15940 case SOUND_CTRL_ID_SIMPLE:
15941 if (setup.sound_simple)
15942 setup.sound_simple = FALSE;
15943 else if (audio.sound_available)
15945 setup.sound = setup.sound_simple = TRUE;
15946 SetAudioMode(setup.sound);