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)
61 #define USE_FIX_CE_ACTION_WITH_PLAYER (USE_NEW_STUFF * 1)
62 #define USE_FIX_NO_ACTION_AFTER_CHANGE (USE_NEW_STUFF * 1)
64 #define USE_PLAYER_REANIMATION (USE_NEW_STUFF * 1)
66 #define USE_GFX_RESET_WHEN_NOT_MOVING (USE_NEW_STUFF * 1)
68 #define USE_NEW_PLAYER_ASSIGNMENTS (USE_NEW_STUFF * 1)
70 #define USE_DELAYED_GFX_REDRAW (USE_NEW_STUFF * 0)
72 #if USE_DELAYED_GFX_REDRAW
73 #define TEST_DrawLevelField(x, y) \
74 GfxRedraw[x][y] |= GFX_REDRAW_TILE
75 #define TEST_DrawLevelFieldCrumbled(x, y) \
76 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
77 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y) \
78 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
79 #define TEST_DrawTwinkleOnField(x, y) \
80 GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
82 #define TEST_DrawLevelField(x, y) \
84 #define TEST_DrawLevelFieldCrumbled(x, y) \
85 DrawLevelFieldCrumbled(x, y)
86 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y) \
87 DrawLevelFieldCrumbledNeighbours(x, y)
88 #define TEST_DrawTwinkleOnField(x, y) \
89 DrawTwinkleOnField(x, y)
98 /* for MovePlayer() */
99 #define MP_NO_ACTION 0
102 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
104 /* for ScrollPlayer() */
105 #define SCROLL_INIT 0
106 #define SCROLL_GO_ON 1
108 /* for Bang()/Explode() */
109 #define EX_PHASE_START 0
110 #define EX_TYPE_NONE 0
111 #define EX_TYPE_NORMAL (1 << 0)
112 #define EX_TYPE_CENTER (1 << 1)
113 #define EX_TYPE_BORDER (1 << 2)
114 #define EX_TYPE_CROSS (1 << 3)
115 #define EX_TYPE_DYNA (1 << 4)
116 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
118 #define PANEL_OFF() (local_player->LevelSolved_PanelOff)
119 #define PANEL_DEACTIVATED(p) ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
120 #define PANEL_XPOS(p) (DX + ALIGNED_TEXT_XPOS(p))
121 #define PANEL_YPOS(p) (DY + ALIGNED_TEXT_YPOS(p))
123 /* special positions in the game control window (relative to control window) */
124 #define XX_LEVEL1 (PANEL_XPOS(game.panel.level))
125 #define XX_LEVEL2 (PANEL_XPOS(game.panel.level) - 1)
126 #define XX_LEVEL (PANEL_XPOS(game.panel.level))
127 #define YY_LEVEL (PANEL_YPOS(game.panel.level))
128 #define XX_EMERALDS (PANEL_XPOS(game.panel.gems))
129 #define YY_EMERALDS (PANEL_YPOS(game.panel.gems))
130 #define XX_DYNAMITE (PANEL_XPOS(game.panel.inventory))
131 #define YY_DYNAMITE (PANEL_YPOS(game.panel.inventory))
132 #define XX_KEYS (PANEL_XPOS(game.panel.keys))
133 #define YY_KEYS (PANEL_YPOS(game.panel.keys))
134 #define XX_SCORE (PANEL_XPOS(game.panel.score))
135 #define YY_SCORE (PANEL_YPOS(game.panel.score))
136 #define XX_TIME1 (PANEL_XPOS(game.panel.time))
137 #define XX_TIME2 (PANEL_XPOS(game.panel.time) + 1)
138 #define XX_TIME (PANEL_XPOS(game.panel.time))
139 #define YY_TIME (PANEL_YPOS(game.panel.time))
141 /* special positions in the game control window (relative to main window) */
142 #define DX_LEVEL1 (DX + XX_LEVEL1)
143 #define DX_LEVEL2 (DX + XX_LEVEL2)
144 #define DX_LEVEL (DX + XX_LEVEL)
145 #define DY_LEVEL (DY + YY_LEVEL)
146 #define DX_EMERALDS (DX + XX_EMERALDS)
147 #define DY_EMERALDS (DY + YY_EMERALDS)
148 #define DX_DYNAMITE (DX + XX_DYNAMITE)
149 #define DY_DYNAMITE (DY + YY_DYNAMITE)
150 #define DX_KEYS (DX + XX_KEYS)
151 #define DY_KEYS (DY + YY_KEYS)
152 #define DX_SCORE (DX + XX_SCORE)
153 #define DY_SCORE (DY + YY_SCORE)
154 #define DX_TIME1 (DX + XX_TIME1)
155 #define DX_TIME2 (DX + XX_TIME2)
156 #define DX_TIME (DX + XX_TIME)
157 #define DY_TIME (DY + YY_TIME)
160 /* game panel display and control definitions */
162 #define GAME_PANEL_LEVEL_NUMBER 0
163 #define GAME_PANEL_GEMS 1
164 #define GAME_PANEL_INVENTORY_COUNT 2
165 #define GAME_PANEL_INVENTORY_FIRST_1 3
166 #define GAME_PANEL_INVENTORY_FIRST_2 4
167 #define GAME_PANEL_INVENTORY_FIRST_3 5
168 #define GAME_PANEL_INVENTORY_FIRST_4 6
169 #define GAME_PANEL_INVENTORY_FIRST_5 7
170 #define GAME_PANEL_INVENTORY_FIRST_6 8
171 #define GAME_PANEL_INVENTORY_FIRST_7 9
172 #define GAME_PANEL_INVENTORY_FIRST_8 10
173 #define GAME_PANEL_INVENTORY_LAST_1 11
174 #define GAME_PANEL_INVENTORY_LAST_2 12
175 #define GAME_PANEL_INVENTORY_LAST_3 13
176 #define GAME_PANEL_INVENTORY_LAST_4 14
177 #define GAME_PANEL_INVENTORY_LAST_5 15
178 #define GAME_PANEL_INVENTORY_LAST_6 16
179 #define GAME_PANEL_INVENTORY_LAST_7 17
180 #define GAME_PANEL_INVENTORY_LAST_8 18
181 #define GAME_PANEL_KEY_1 19
182 #define GAME_PANEL_KEY_2 20
183 #define GAME_PANEL_KEY_3 21
184 #define GAME_PANEL_KEY_4 22
185 #define GAME_PANEL_KEY_5 23
186 #define GAME_PANEL_KEY_6 24
187 #define GAME_PANEL_KEY_7 25
188 #define GAME_PANEL_KEY_8 26
189 #define GAME_PANEL_KEY_WHITE 27
190 #define GAME_PANEL_KEY_WHITE_COUNT 28
191 #define GAME_PANEL_SCORE 29
192 #define GAME_PANEL_HIGHSCORE 30
193 #define GAME_PANEL_TIME 31
194 #define GAME_PANEL_TIME_HH 32
195 #define GAME_PANEL_TIME_MM 33
196 #define GAME_PANEL_TIME_SS 34
197 #define GAME_PANEL_FRAME 35
198 #define GAME_PANEL_SHIELD_NORMAL 36
199 #define GAME_PANEL_SHIELD_NORMAL_TIME 37
200 #define GAME_PANEL_SHIELD_DEADLY 38
201 #define GAME_PANEL_SHIELD_DEADLY_TIME 39
202 #define GAME_PANEL_EXIT 40
203 #define GAME_PANEL_EMC_MAGIC_BALL 41
204 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH 42
205 #define GAME_PANEL_LIGHT_SWITCH 43
206 #define GAME_PANEL_LIGHT_SWITCH_TIME 44
207 #define GAME_PANEL_TIMEGATE_SWITCH 45
208 #define GAME_PANEL_TIMEGATE_SWITCH_TIME 46
209 #define GAME_PANEL_SWITCHGATE_SWITCH 47
210 #define GAME_PANEL_EMC_LENSES 48
211 #define GAME_PANEL_EMC_LENSES_TIME 49
212 #define GAME_PANEL_EMC_MAGNIFIER 50
213 #define GAME_PANEL_EMC_MAGNIFIER_TIME 51
214 #define GAME_PANEL_BALLOON_SWITCH 52
215 #define GAME_PANEL_DYNABOMB_NUMBER 53
216 #define GAME_PANEL_DYNABOMB_SIZE 54
217 #define GAME_PANEL_DYNABOMB_POWER 55
218 #define GAME_PANEL_PENGUINS 56
219 #define GAME_PANEL_SOKOBAN_OBJECTS 57
220 #define GAME_PANEL_SOKOBAN_FIELDS 58
221 #define GAME_PANEL_ROBOT_WHEEL 59
222 #define GAME_PANEL_CONVEYOR_BELT_1 60
223 #define GAME_PANEL_CONVEYOR_BELT_2 61
224 #define GAME_PANEL_CONVEYOR_BELT_3 62
225 #define GAME_PANEL_CONVEYOR_BELT_4 63
226 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH 64
227 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH 65
228 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH 66
229 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH 67
230 #define GAME_PANEL_MAGIC_WALL 68
231 #define GAME_PANEL_MAGIC_WALL_TIME 69
232 #define GAME_PANEL_GRAVITY_STATE 70
233 #define GAME_PANEL_GRAPHIC_1 71
234 #define GAME_PANEL_GRAPHIC_2 72
235 #define GAME_PANEL_GRAPHIC_3 73
236 #define GAME_PANEL_GRAPHIC_4 74
237 #define GAME_PANEL_GRAPHIC_5 75
238 #define GAME_PANEL_GRAPHIC_6 76
239 #define GAME_PANEL_GRAPHIC_7 77
240 #define GAME_PANEL_GRAPHIC_8 78
241 #define GAME_PANEL_ELEMENT_1 79
242 #define GAME_PANEL_ELEMENT_2 80
243 #define GAME_PANEL_ELEMENT_3 81
244 #define GAME_PANEL_ELEMENT_4 82
245 #define GAME_PANEL_ELEMENT_5 83
246 #define GAME_PANEL_ELEMENT_6 84
247 #define GAME_PANEL_ELEMENT_7 85
248 #define GAME_PANEL_ELEMENT_8 86
249 #define GAME_PANEL_ELEMENT_COUNT_1 87
250 #define GAME_PANEL_ELEMENT_COUNT_2 88
251 #define GAME_PANEL_ELEMENT_COUNT_3 89
252 #define GAME_PANEL_ELEMENT_COUNT_4 90
253 #define GAME_PANEL_ELEMENT_COUNT_5 91
254 #define GAME_PANEL_ELEMENT_COUNT_6 92
255 #define GAME_PANEL_ELEMENT_COUNT_7 93
256 #define GAME_PANEL_ELEMENT_COUNT_8 94
257 #define GAME_PANEL_CE_SCORE_1 95
258 #define GAME_PANEL_CE_SCORE_2 96
259 #define GAME_PANEL_CE_SCORE_3 97
260 #define GAME_PANEL_CE_SCORE_4 98
261 #define GAME_PANEL_CE_SCORE_5 99
262 #define GAME_PANEL_CE_SCORE_6 100
263 #define GAME_PANEL_CE_SCORE_7 101
264 #define GAME_PANEL_CE_SCORE_8 102
265 #define GAME_PANEL_CE_SCORE_1_ELEMENT 103
266 #define GAME_PANEL_CE_SCORE_2_ELEMENT 104
267 #define GAME_PANEL_CE_SCORE_3_ELEMENT 105
268 #define GAME_PANEL_CE_SCORE_4_ELEMENT 106
269 #define GAME_PANEL_CE_SCORE_5_ELEMENT 107
270 #define GAME_PANEL_CE_SCORE_6_ELEMENT 108
271 #define GAME_PANEL_CE_SCORE_7_ELEMENT 109
272 #define GAME_PANEL_CE_SCORE_8_ELEMENT 110
273 #define GAME_PANEL_PLAYER_NAME 111
274 #define GAME_PANEL_LEVEL_NAME 112
275 #define GAME_PANEL_LEVEL_AUTHOR 113
277 #define NUM_GAME_PANEL_CONTROLS 114
279 struct GamePanelOrderInfo
285 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
287 struct GamePanelControlInfo
291 struct TextPosInfo *pos;
294 int value, last_value;
295 int frame, last_frame;
300 static struct GamePanelControlInfo game_panel_controls[] =
303 GAME_PANEL_LEVEL_NUMBER,
304 &game.panel.level_number,
313 GAME_PANEL_INVENTORY_COUNT,
314 &game.panel.inventory_count,
318 GAME_PANEL_INVENTORY_FIRST_1,
319 &game.panel.inventory_first[0],
323 GAME_PANEL_INVENTORY_FIRST_2,
324 &game.panel.inventory_first[1],
328 GAME_PANEL_INVENTORY_FIRST_3,
329 &game.panel.inventory_first[2],
333 GAME_PANEL_INVENTORY_FIRST_4,
334 &game.panel.inventory_first[3],
338 GAME_PANEL_INVENTORY_FIRST_5,
339 &game.panel.inventory_first[4],
343 GAME_PANEL_INVENTORY_FIRST_6,
344 &game.panel.inventory_first[5],
348 GAME_PANEL_INVENTORY_FIRST_7,
349 &game.panel.inventory_first[6],
353 GAME_PANEL_INVENTORY_FIRST_8,
354 &game.panel.inventory_first[7],
358 GAME_PANEL_INVENTORY_LAST_1,
359 &game.panel.inventory_last[0],
363 GAME_PANEL_INVENTORY_LAST_2,
364 &game.panel.inventory_last[1],
368 GAME_PANEL_INVENTORY_LAST_3,
369 &game.panel.inventory_last[2],
373 GAME_PANEL_INVENTORY_LAST_4,
374 &game.panel.inventory_last[3],
378 GAME_PANEL_INVENTORY_LAST_5,
379 &game.panel.inventory_last[4],
383 GAME_PANEL_INVENTORY_LAST_6,
384 &game.panel.inventory_last[5],
388 GAME_PANEL_INVENTORY_LAST_7,
389 &game.panel.inventory_last[6],
393 GAME_PANEL_INVENTORY_LAST_8,
394 &game.panel.inventory_last[7],
438 GAME_PANEL_KEY_WHITE,
439 &game.panel.key_white,
443 GAME_PANEL_KEY_WHITE_COUNT,
444 &game.panel.key_white_count,
453 GAME_PANEL_HIGHSCORE,
454 &game.panel.highscore,
483 GAME_PANEL_SHIELD_NORMAL,
484 &game.panel.shield_normal,
488 GAME_PANEL_SHIELD_NORMAL_TIME,
489 &game.panel.shield_normal_time,
493 GAME_PANEL_SHIELD_DEADLY,
494 &game.panel.shield_deadly,
498 GAME_PANEL_SHIELD_DEADLY_TIME,
499 &game.panel.shield_deadly_time,
508 GAME_PANEL_EMC_MAGIC_BALL,
509 &game.panel.emc_magic_ball,
513 GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
514 &game.panel.emc_magic_ball_switch,
518 GAME_PANEL_LIGHT_SWITCH,
519 &game.panel.light_switch,
523 GAME_PANEL_LIGHT_SWITCH_TIME,
524 &game.panel.light_switch_time,
528 GAME_PANEL_TIMEGATE_SWITCH,
529 &game.panel.timegate_switch,
533 GAME_PANEL_TIMEGATE_SWITCH_TIME,
534 &game.panel.timegate_switch_time,
538 GAME_PANEL_SWITCHGATE_SWITCH,
539 &game.panel.switchgate_switch,
543 GAME_PANEL_EMC_LENSES,
544 &game.panel.emc_lenses,
548 GAME_PANEL_EMC_LENSES_TIME,
549 &game.panel.emc_lenses_time,
553 GAME_PANEL_EMC_MAGNIFIER,
554 &game.panel.emc_magnifier,
558 GAME_PANEL_EMC_MAGNIFIER_TIME,
559 &game.panel.emc_magnifier_time,
563 GAME_PANEL_BALLOON_SWITCH,
564 &game.panel.balloon_switch,
568 GAME_PANEL_DYNABOMB_NUMBER,
569 &game.panel.dynabomb_number,
573 GAME_PANEL_DYNABOMB_SIZE,
574 &game.panel.dynabomb_size,
578 GAME_PANEL_DYNABOMB_POWER,
579 &game.panel.dynabomb_power,
584 &game.panel.penguins,
588 GAME_PANEL_SOKOBAN_OBJECTS,
589 &game.panel.sokoban_objects,
593 GAME_PANEL_SOKOBAN_FIELDS,
594 &game.panel.sokoban_fields,
598 GAME_PANEL_ROBOT_WHEEL,
599 &game.panel.robot_wheel,
603 GAME_PANEL_CONVEYOR_BELT_1,
604 &game.panel.conveyor_belt[0],
608 GAME_PANEL_CONVEYOR_BELT_2,
609 &game.panel.conveyor_belt[1],
613 GAME_PANEL_CONVEYOR_BELT_3,
614 &game.panel.conveyor_belt[2],
618 GAME_PANEL_CONVEYOR_BELT_4,
619 &game.panel.conveyor_belt[3],
623 GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
624 &game.panel.conveyor_belt_switch[0],
628 GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
629 &game.panel.conveyor_belt_switch[1],
633 GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
634 &game.panel.conveyor_belt_switch[2],
638 GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
639 &game.panel.conveyor_belt_switch[3],
643 GAME_PANEL_MAGIC_WALL,
644 &game.panel.magic_wall,
648 GAME_PANEL_MAGIC_WALL_TIME,
649 &game.panel.magic_wall_time,
653 GAME_PANEL_GRAVITY_STATE,
654 &game.panel.gravity_state,
658 GAME_PANEL_GRAPHIC_1,
659 &game.panel.graphic[0],
663 GAME_PANEL_GRAPHIC_2,
664 &game.panel.graphic[1],
668 GAME_PANEL_GRAPHIC_3,
669 &game.panel.graphic[2],
673 GAME_PANEL_GRAPHIC_4,
674 &game.panel.graphic[3],
678 GAME_PANEL_GRAPHIC_5,
679 &game.panel.graphic[4],
683 GAME_PANEL_GRAPHIC_6,
684 &game.panel.graphic[5],
688 GAME_PANEL_GRAPHIC_7,
689 &game.panel.graphic[6],
693 GAME_PANEL_GRAPHIC_8,
694 &game.panel.graphic[7],
698 GAME_PANEL_ELEMENT_1,
699 &game.panel.element[0],
703 GAME_PANEL_ELEMENT_2,
704 &game.panel.element[1],
708 GAME_PANEL_ELEMENT_3,
709 &game.panel.element[2],
713 GAME_PANEL_ELEMENT_4,
714 &game.panel.element[3],
718 GAME_PANEL_ELEMENT_5,
719 &game.panel.element[4],
723 GAME_PANEL_ELEMENT_6,
724 &game.panel.element[5],
728 GAME_PANEL_ELEMENT_7,
729 &game.panel.element[6],
733 GAME_PANEL_ELEMENT_8,
734 &game.panel.element[7],
738 GAME_PANEL_ELEMENT_COUNT_1,
739 &game.panel.element_count[0],
743 GAME_PANEL_ELEMENT_COUNT_2,
744 &game.panel.element_count[1],
748 GAME_PANEL_ELEMENT_COUNT_3,
749 &game.panel.element_count[2],
753 GAME_PANEL_ELEMENT_COUNT_4,
754 &game.panel.element_count[3],
758 GAME_PANEL_ELEMENT_COUNT_5,
759 &game.panel.element_count[4],
763 GAME_PANEL_ELEMENT_COUNT_6,
764 &game.panel.element_count[5],
768 GAME_PANEL_ELEMENT_COUNT_7,
769 &game.panel.element_count[6],
773 GAME_PANEL_ELEMENT_COUNT_8,
774 &game.panel.element_count[7],
778 GAME_PANEL_CE_SCORE_1,
779 &game.panel.ce_score[0],
783 GAME_PANEL_CE_SCORE_2,
784 &game.panel.ce_score[1],
788 GAME_PANEL_CE_SCORE_3,
789 &game.panel.ce_score[2],
793 GAME_PANEL_CE_SCORE_4,
794 &game.panel.ce_score[3],
798 GAME_PANEL_CE_SCORE_5,
799 &game.panel.ce_score[4],
803 GAME_PANEL_CE_SCORE_6,
804 &game.panel.ce_score[5],
808 GAME_PANEL_CE_SCORE_7,
809 &game.panel.ce_score[6],
813 GAME_PANEL_CE_SCORE_8,
814 &game.panel.ce_score[7],
818 GAME_PANEL_CE_SCORE_1_ELEMENT,
819 &game.panel.ce_score_element[0],
823 GAME_PANEL_CE_SCORE_2_ELEMENT,
824 &game.panel.ce_score_element[1],
828 GAME_PANEL_CE_SCORE_3_ELEMENT,
829 &game.panel.ce_score_element[2],
833 GAME_PANEL_CE_SCORE_4_ELEMENT,
834 &game.panel.ce_score_element[3],
838 GAME_PANEL_CE_SCORE_5_ELEMENT,
839 &game.panel.ce_score_element[4],
843 GAME_PANEL_CE_SCORE_6_ELEMENT,
844 &game.panel.ce_score_element[5],
848 GAME_PANEL_CE_SCORE_7_ELEMENT,
849 &game.panel.ce_score_element[6],
853 GAME_PANEL_CE_SCORE_8_ELEMENT,
854 &game.panel.ce_score_element[7],
858 GAME_PANEL_PLAYER_NAME,
859 &game.panel.player_name,
863 GAME_PANEL_LEVEL_NAME,
864 &game.panel.level_name,
868 GAME_PANEL_LEVEL_AUTHOR,
869 &game.panel.level_author,
882 /* values for delayed check of falling and moving elements and for collision */
883 #define CHECK_DELAY_MOVING 3
884 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
885 #define CHECK_DELAY_COLLISION 2
886 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
888 /* values for initial player move delay (initial delay counter value) */
889 #define INITIAL_MOVE_DELAY_OFF -1
890 #define INITIAL_MOVE_DELAY_ON 0
892 /* values for player movement speed (which is in fact a delay value) */
893 #define MOVE_DELAY_MIN_SPEED 32
894 #define MOVE_DELAY_NORMAL_SPEED 8
895 #define MOVE_DELAY_HIGH_SPEED 4
896 #define MOVE_DELAY_MAX_SPEED 1
898 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
899 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
901 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
902 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
904 /* values for other actions */
905 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
906 #define MOVE_STEPSIZE_MIN (1)
907 #define MOVE_STEPSIZE_MAX (TILEX)
909 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
910 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
912 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
914 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
915 RND(element_info[e].push_delay_random))
916 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
917 RND(element_info[e].drop_delay_random))
918 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
919 RND(element_info[e].move_delay_random))
920 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
921 (element_info[e].move_delay_random))
922 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
923 RND(element_info[e].ce_value_random_initial))
924 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
925 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
926 RND((c)->delay_random * (c)->delay_frames))
927 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
928 RND((c)->delay_random))
931 #define GET_VALID_RUNTIME_ELEMENT(e) \
932 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
934 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
935 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
936 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
937 (be) + (e) - EL_SELF)
939 #define GET_PLAYER_FROM_BITS(p) \
940 (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
942 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
943 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
944 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
945 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
946 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
947 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
948 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
949 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
950 RESOLVED_REFERENCE_ELEMENT(be, e) : \
953 #define CAN_GROW_INTO(e) \
954 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
956 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
957 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
960 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
961 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
962 (CAN_MOVE_INTO_ACID(e) && \
963 Feld[x][y] == EL_ACID) || \
966 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
967 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
968 (CAN_MOVE_INTO_ACID(e) && \
969 Feld[x][y] == EL_ACID) || \
972 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
973 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
975 (CAN_MOVE_INTO_ACID(e) && \
976 Feld[x][y] == EL_ACID) || \
977 (DONT_COLLIDE_WITH(e) && \
979 !PLAYER_ENEMY_PROTECTED(x, y))))
981 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
982 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
984 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
985 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
987 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
988 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
990 #define ANDROID_CAN_CLONE_FIELD(x, y) \
991 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
992 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
994 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
995 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
997 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
998 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
1000 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
1001 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
1003 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
1004 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
1006 #define PIG_CAN_ENTER_FIELD(e, x, y) \
1007 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
1009 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
1010 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
1011 Feld[x][y] == EL_EM_EXIT_OPEN || \
1012 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
1013 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
1014 IS_FOOD_PENGUIN(Feld[x][y])))
1015 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
1016 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1018 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
1019 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
1021 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
1022 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1024 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
1025 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
1026 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
1028 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
1030 #define CE_ENTER_FIELD_COND(e, x, y) \
1031 (!IS_PLAYER(x, y) && \
1032 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
1034 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
1035 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1037 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
1038 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1040 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
1041 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
1042 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
1043 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1045 /* game button identifiers */
1046 #define GAME_CTRL_ID_STOP 0
1047 #define GAME_CTRL_ID_PAUSE 1
1048 #define GAME_CTRL_ID_PLAY 2
1049 #define SOUND_CTRL_ID_MUSIC 3
1050 #define SOUND_CTRL_ID_LOOPS 4
1051 #define SOUND_CTRL_ID_SIMPLE 5
1053 #define NUM_GAME_BUTTONS 6
1056 /* forward declaration for internal use */
1058 static void CreateField(int, int, int);
1060 static void ResetGfxAnimation(int, int);
1062 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1063 static void AdvanceFrameAndPlayerCounters(int);
1065 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1066 static boolean MovePlayer(struct PlayerInfo *, int, int);
1067 static void ScrollPlayer(struct PlayerInfo *, int);
1068 static void ScrollScreen(struct PlayerInfo *, int);
1070 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1071 static boolean DigFieldByCE(int, int, int);
1072 static boolean SnapField(struct PlayerInfo *, int, int);
1073 static boolean DropElement(struct PlayerInfo *);
1075 static void InitBeltMovement(void);
1076 static void CloseAllOpenTimegates(void);
1077 static void CheckGravityMovement(struct PlayerInfo *);
1078 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1079 static void KillPlayerUnlessEnemyProtected(int, int);
1080 static void KillPlayerUnlessExplosionProtected(int, int);
1082 static void TestIfPlayerTouchesCustomElement(int, int);
1083 static void TestIfElementTouchesCustomElement(int, int);
1084 static void TestIfElementHitsCustomElement(int, int, int);
1086 static void TestIfElementSmashesCustomElement(int, int, int);
1089 static void HandleElementChange(int, int, int);
1090 static void ExecuteCustomElementAction(int, int, int, int);
1091 static boolean ChangeElement(int, int, int, int);
1093 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1094 #define CheckTriggeredElementChange(x, y, e, ev) \
1095 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1096 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
1097 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1098 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
1099 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1100 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
1101 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1103 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1104 #define CheckElementChange(x, y, e, te, ev) \
1105 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1106 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
1107 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1108 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
1109 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1111 static void PlayLevelSound(int, int, int);
1112 static void PlayLevelSoundNearest(int, int, int);
1113 static void PlayLevelSoundAction(int, int, int);
1114 static void PlayLevelSoundElementAction(int, int, int, int);
1115 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1116 static void PlayLevelSoundActionIfLoop(int, int, int);
1117 static void StopLevelSoundActionIfLoop(int, int, int);
1118 static void PlayLevelMusic();
1120 static void MapGameButtons();
1121 static void HandleGameButtons(struct GadgetInfo *);
1123 int AmoebeNachbarNr(int, int);
1124 void AmoebeUmwandeln(int, int);
1125 void ContinueMoving(int, int);
1126 void Bang(int, int);
1127 void InitMovDir(int, int);
1128 void InitAmoebaNr(int, int);
1129 int NewHiScore(void);
1131 void TestIfGoodThingHitsBadThing(int, int, int);
1132 void TestIfBadThingHitsGoodThing(int, int, int);
1133 void TestIfPlayerTouchesBadThing(int, int);
1134 void TestIfPlayerRunsIntoBadThing(int, int, int);
1135 void TestIfBadThingTouchesPlayer(int, int);
1136 void TestIfBadThingRunsIntoPlayer(int, int, int);
1137 void TestIfFriendTouchesBadThing(int, int);
1138 void TestIfBadThingTouchesFriend(int, int);
1139 void TestIfBadThingTouchesOtherBadThing(int, int);
1140 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1142 void KillPlayer(struct PlayerInfo *);
1143 void BuryPlayer(struct PlayerInfo *);
1144 void RemovePlayer(struct PlayerInfo *);
1146 static int getInvisibleActiveFromInvisibleElement(int);
1147 static int getInvisibleFromInvisibleActiveElement(int);
1149 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1151 /* for detection of endless loops, caused by custom element programming */
1152 /* (using maximal playfield width x 10 is just a rough approximation) */
1153 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
1155 #define RECURSION_LOOP_DETECTION_START(e, rc) \
1157 if (recursion_loop_detected) \
1160 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
1162 recursion_loop_detected = TRUE; \
1163 recursion_loop_element = (e); \
1166 recursion_loop_depth++; \
1169 #define RECURSION_LOOP_DETECTION_END() \
1171 recursion_loop_depth--; \
1174 static int recursion_loop_depth;
1175 static boolean recursion_loop_detected;
1176 static boolean recursion_loop_element;
1178 static int map_player_action[MAX_PLAYERS];
1181 /* ------------------------------------------------------------------------- */
1182 /* definition of elements that automatically change to other elements after */
1183 /* a specified time, eventually calling a function when changing */
1184 /* ------------------------------------------------------------------------- */
1186 /* forward declaration for changer functions */
1187 static void InitBuggyBase(int, int);
1188 static void WarnBuggyBase(int, int);
1190 static void InitTrap(int, int);
1191 static void ActivateTrap(int, int);
1192 static void ChangeActiveTrap(int, int);
1194 static void InitRobotWheel(int, int);
1195 static void RunRobotWheel(int, int);
1196 static void StopRobotWheel(int, int);
1198 static void InitTimegateWheel(int, int);
1199 static void RunTimegateWheel(int, int);
1201 static void InitMagicBallDelay(int, int);
1202 static void ActivateMagicBall(int, int);
1204 struct ChangingElementInfo
1209 void (*pre_change_function)(int x, int y);
1210 void (*change_function)(int x, int y);
1211 void (*post_change_function)(int x, int y);
1214 static struct ChangingElementInfo change_delay_list[] =
1249 EL_STEEL_EXIT_OPENING,
1257 EL_STEEL_EXIT_CLOSING,
1258 EL_STEEL_EXIT_CLOSED,
1285 EL_EM_STEEL_EXIT_OPENING,
1286 EL_EM_STEEL_EXIT_OPEN,
1293 EL_EM_STEEL_EXIT_CLOSING,
1297 EL_EM_STEEL_EXIT_CLOSED,
1321 EL_SWITCHGATE_OPENING,
1329 EL_SWITCHGATE_CLOSING,
1330 EL_SWITCHGATE_CLOSED,
1337 EL_TIMEGATE_OPENING,
1345 EL_TIMEGATE_CLOSING,
1354 EL_ACID_SPLASH_LEFT,
1362 EL_ACID_SPLASH_RIGHT,
1371 EL_SP_BUGGY_BASE_ACTIVATING,
1378 EL_SP_BUGGY_BASE_ACTIVATING,
1379 EL_SP_BUGGY_BASE_ACTIVE,
1386 EL_SP_BUGGY_BASE_ACTIVE,
1410 EL_ROBOT_WHEEL_ACTIVE,
1418 EL_TIMEGATE_SWITCH_ACTIVE,
1426 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1427 EL_DC_TIMEGATE_SWITCH,
1434 EL_EMC_MAGIC_BALL_ACTIVE,
1435 EL_EMC_MAGIC_BALL_ACTIVE,
1442 EL_EMC_SPRING_BUMPER_ACTIVE,
1443 EL_EMC_SPRING_BUMPER,
1450 EL_DIAGONAL_SHRINKING,
1458 EL_DIAGONAL_GROWING,
1479 int push_delay_fixed, push_delay_random;
1483 { EL_SPRING, 0, 0 },
1484 { EL_BALLOON, 0, 0 },
1486 { EL_SOKOBAN_OBJECT, 2, 0 },
1487 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1488 { EL_SATELLITE, 2, 0 },
1489 { EL_SP_DISK_YELLOW, 2, 0 },
1491 { EL_UNDEFINED, 0, 0 },
1499 move_stepsize_list[] =
1501 { EL_AMOEBA_DROP, 2 },
1502 { EL_AMOEBA_DROPPING, 2 },
1503 { EL_QUICKSAND_FILLING, 1 },
1504 { EL_QUICKSAND_EMPTYING, 1 },
1505 { EL_QUICKSAND_FAST_FILLING, 2 },
1506 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1507 { EL_MAGIC_WALL_FILLING, 2 },
1508 { EL_MAGIC_WALL_EMPTYING, 2 },
1509 { EL_BD_MAGIC_WALL_FILLING, 2 },
1510 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1511 { EL_DC_MAGIC_WALL_FILLING, 2 },
1512 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1514 { EL_UNDEFINED, 0 },
1522 collect_count_list[] =
1525 { EL_BD_DIAMOND, 1 },
1526 { EL_EMERALD_YELLOW, 1 },
1527 { EL_EMERALD_RED, 1 },
1528 { EL_EMERALD_PURPLE, 1 },
1530 { EL_SP_INFOTRON, 1 },
1534 { EL_UNDEFINED, 0 },
1542 access_direction_list[] =
1544 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1545 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1546 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1547 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1548 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1549 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1550 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1551 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1552 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1553 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1554 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1556 { EL_SP_PORT_LEFT, MV_RIGHT },
1557 { EL_SP_PORT_RIGHT, MV_LEFT },
1558 { EL_SP_PORT_UP, MV_DOWN },
1559 { EL_SP_PORT_DOWN, MV_UP },
1560 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1561 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1562 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1563 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1564 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1565 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1566 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1567 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1568 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1569 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1570 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1571 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1572 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1573 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1574 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1576 { EL_UNDEFINED, MV_NONE }
1579 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1581 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1582 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1583 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1584 IS_JUST_CHANGING(x, y))
1586 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1588 /* static variables for playfield scan mode (scanning forward or backward) */
1589 static int playfield_scan_start_x = 0;
1590 static int playfield_scan_start_y = 0;
1591 static int playfield_scan_delta_x = 1;
1592 static int playfield_scan_delta_y = 1;
1594 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1595 (y) >= 0 && (y) <= lev_fieldy - 1; \
1596 (y) += playfield_scan_delta_y) \
1597 for ((x) = playfield_scan_start_x; \
1598 (x) >= 0 && (x) <= lev_fieldx - 1; \
1599 (x) += playfield_scan_delta_x)
1602 void DEBUG_SetMaximumDynamite()
1606 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1607 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1608 local_player->inventory_element[local_player->inventory_size++] =
1613 static void InitPlayfieldScanModeVars()
1615 if (game.use_reverse_scan_direction)
1617 playfield_scan_start_x = lev_fieldx - 1;
1618 playfield_scan_start_y = lev_fieldy - 1;
1620 playfield_scan_delta_x = -1;
1621 playfield_scan_delta_y = -1;
1625 playfield_scan_start_x = 0;
1626 playfield_scan_start_y = 0;
1628 playfield_scan_delta_x = 1;
1629 playfield_scan_delta_y = 1;
1633 static void InitPlayfieldScanMode(int mode)
1635 game.use_reverse_scan_direction =
1636 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1638 InitPlayfieldScanModeVars();
1641 static int get_move_delay_from_stepsize(int move_stepsize)
1644 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1646 /* make sure that stepsize value is always a power of 2 */
1647 move_stepsize = (1 << log_2(move_stepsize));
1649 return TILEX / move_stepsize;
1652 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1655 int player_nr = player->index_nr;
1656 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1657 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1659 /* do no immediately change move delay -- the player might just be moving */
1660 player->move_delay_value_next = move_delay;
1662 /* information if player can move must be set separately */
1663 player->cannot_move = cannot_move;
1667 player->move_delay = game.initial_move_delay[player_nr];
1668 player->move_delay_value = game.initial_move_delay_value[player_nr];
1670 player->move_delay_value_next = -1;
1672 player->move_delay_reset_counter = 0;
1676 void GetPlayerConfig()
1678 GameFrameDelay = setup.game_frame_delay;
1680 if (!audio.sound_available)
1681 setup.sound_simple = FALSE;
1683 if (!audio.loops_available)
1684 setup.sound_loops = FALSE;
1686 if (!audio.music_available)
1687 setup.sound_music = FALSE;
1689 if (!video.fullscreen_available)
1690 setup.fullscreen = FALSE;
1692 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1694 SetAudioMode(setup.sound);
1698 int GetElementFromGroupElement(int element)
1700 if (IS_GROUP_ELEMENT(element))
1702 struct ElementGroupInfo *group = element_info[element].group;
1703 int last_anim_random_frame = gfx.anim_random_frame;
1706 if (group->choice_mode == ANIM_RANDOM)
1707 gfx.anim_random_frame = RND(group->num_elements_resolved);
1709 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1710 group->choice_mode, 0,
1713 if (group->choice_mode == ANIM_RANDOM)
1714 gfx.anim_random_frame = last_anim_random_frame;
1716 group->choice_pos++;
1718 element = group->element_resolved[element_pos];
1724 static void InitPlayerField(int x, int y, int element, boolean init_game)
1726 if (element == EL_SP_MURPHY)
1730 if (stored_player[0].present)
1732 Feld[x][y] = EL_SP_MURPHY_CLONE;
1738 stored_player[0].initial_element = element;
1739 stored_player[0].use_murphy = TRUE;
1741 if (!level.use_artwork_element[0])
1742 stored_player[0].artwork_element = EL_SP_MURPHY;
1745 Feld[x][y] = EL_PLAYER_1;
1751 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1752 int jx = player->jx, jy = player->jy;
1754 player->present = TRUE;
1756 player->block_last_field = (element == EL_SP_MURPHY ?
1757 level.sp_block_last_field :
1758 level.block_last_field);
1760 /* ---------- initialize player's last field block delay --------------- */
1762 /* always start with reliable default value (no adjustment needed) */
1763 player->block_delay_adjustment = 0;
1765 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1766 if (player->block_last_field && element == EL_SP_MURPHY)
1767 player->block_delay_adjustment = 1;
1769 /* special case 2: in game engines before 3.1.1, blocking was different */
1770 if (game.use_block_last_field_bug)
1771 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1773 if (!options.network || player->connected)
1775 player->active = TRUE;
1777 /* remove potentially duplicate players */
1778 if (StorePlayer[jx][jy] == Feld[x][y])
1779 StorePlayer[jx][jy] = 0;
1781 StorePlayer[x][y] = Feld[x][y];
1785 printf("Player %d activated.\n", player->element_nr);
1786 printf("[Local player is %d and currently %s.]\n",
1787 local_player->element_nr,
1788 local_player->active ? "active" : "not active");
1792 Feld[x][y] = EL_EMPTY;
1794 player->jx = player->last_jx = x;
1795 player->jy = player->last_jy = y;
1798 #if USE_PLAYER_REANIMATION
1801 int player_nr = GET_PLAYER_NR(element);
1802 struct PlayerInfo *player = &stored_player[player_nr];
1804 if (player->active && player->killed)
1805 player->reanimated = TRUE; /* if player was just killed, reanimate him */
1810 static void InitField(int x, int y, boolean init_game)
1812 int element = Feld[x][y];
1821 InitPlayerField(x, y, element, init_game);
1824 case EL_SOKOBAN_FIELD_PLAYER:
1825 element = Feld[x][y] = EL_PLAYER_1;
1826 InitField(x, y, init_game);
1828 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1829 InitField(x, y, init_game);
1832 case EL_SOKOBAN_FIELD_EMPTY:
1833 local_player->sokobanfields_still_needed++;
1837 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1838 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1839 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1840 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1841 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1842 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1843 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1844 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1845 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1846 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1855 case EL_SPACESHIP_RIGHT:
1856 case EL_SPACESHIP_UP:
1857 case EL_SPACESHIP_LEFT:
1858 case EL_SPACESHIP_DOWN:
1859 case EL_BD_BUTTERFLY:
1860 case EL_BD_BUTTERFLY_RIGHT:
1861 case EL_BD_BUTTERFLY_UP:
1862 case EL_BD_BUTTERFLY_LEFT:
1863 case EL_BD_BUTTERFLY_DOWN:
1865 case EL_BD_FIREFLY_RIGHT:
1866 case EL_BD_FIREFLY_UP:
1867 case EL_BD_FIREFLY_LEFT:
1868 case EL_BD_FIREFLY_DOWN:
1869 case EL_PACMAN_RIGHT:
1871 case EL_PACMAN_LEFT:
1872 case EL_PACMAN_DOWN:
1874 case EL_YAMYAM_LEFT:
1875 case EL_YAMYAM_RIGHT:
1877 case EL_YAMYAM_DOWN:
1878 case EL_DARK_YAMYAM:
1881 case EL_SP_SNIKSNAK:
1882 case EL_SP_ELECTRON:
1891 case EL_AMOEBA_FULL:
1896 case EL_AMOEBA_DROP:
1897 if (y == lev_fieldy - 1)
1899 Feld[x][y] = EL_AMOEBA_GROWING;
1900 Store[x][y] = EL_AMOEBA_WET;
1904 case EL_DYNAMITE_ACTIVE:
1905 case EL_SP_DISK_RED_ACTIVE:
1906 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1907 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1908 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1909 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1910 MovDelay[x][y] = 96;
1913 case EL_EM_DYNAMITE_ACTIVE:
1914 MovDelay[x][y] = 32;
1918 local_player->lights_still_needed++;
1922 local_player->friends_still_needed++;
1927 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1930 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1931 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1932 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1933 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1934 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1935 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1936 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1937 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1938 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1939 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1940 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1941 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1944 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1945 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1946 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1948 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1950 game.belt_dir[belt_nr] = belt_dir;
1951 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1953 else /* more than one switch -- set it like the first switch */
1955 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1960 #if !USE_BOTH_SWITCHGATE_SWITCHES
1961 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1963 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1966 case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1968 Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1972 case EL_LIGHT_SWITCH_ACTIVE:
1974 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1977 case EL_INVISIBLE_STEELWALL:
1978 case EL_INVISIBLE_WALL:
1979 case EL_INVISIBLE_SAND:
1980 if (game.light_time_left > 0 ||
1981 game.lenses_time_left > 0)
1982 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1985 case EL_EMC_MAGIC_BALL:
1986 if (game.ball_state)
1987 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1990 case EL_EMC_MAGIC_BALL_SWITCH:
1991 if (game.ball_state)
1992 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1995 case EL_TRIGGER_PLAYER:
1996 case EL_TRIGGER_ELEMENT:
1997 case EL_TRIGGER_CE_VALUE:
1998 case EL_TRIGGER_CE_SCORE:
2000 case EL_ANY_ELEMENT:
2001 case EL_CURRENT_CE_VALUE:
2002 case EL_CURRENT_CE_SCORE:
2019 /* reference elements should not be used on the playfield */
2020 Feld[x][y] = EL_EMPTY;
2024 if (IS_CUSTOM_ELEMENT(element))
2026 if (CAN_MOVE(element))
2029 #if USE_NEW_CUSTOM_VALUE
2030 if (!element_info[element].use_last_ce_value || init_game)
2031 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2034 else if (IS_GROUP_ELEMENT(element))
2036 Feld[x][y] = GetElementFromGroupElement(element);
2038 InitField(x, y, init_game);
2045 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2048 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2050 InitField(x, y, init_game);
2052 /* not needed to call InitMovDir() -- already done by InitField()! */
2053 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2054 CAN_MOVE(Feld[x][y]))
2058 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2060 int old_element = Feld[x][y];
2062 InitField(x, y, init_game);
2064 /* not needed to call InitMovDir() -- already done by InitField()! */
2065 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2066 CAN_MOVE(old_element) &&
2067 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2070 /* this case is in fact a combination of not less than three bugs:
2071 first, it calls InitMovDir() for elements that can move, although this is
2072 already done by InitField(); then, it checks the element that was at this
2073 field _before_ the call to InitField() (which can change it); lastly, it
2074 was not called for "mole with direction" elements, which were treated as
2075 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2081 static int get_key_element_from_nr(int key_nr)
2083 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2084 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2085 EL_EM_KEY_1 : EL_KEY_1);
2087 return key_base_element + key_nr;
2090 static int get_next_dropped_element(struct PlayerInfo *player)
2092 return (player->inventory_size > 0 ?
2093 player->inventory_element[player->inventory_size - 1] :
2094 player->inventory_infinite_element != EL_UNDEFINED ?
2095 player->inventory_infinite_element :
2096 player->dynabombs_left > 0 ?
2097 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2101 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2103 /* pos >= 0: get element from bottom of the stack;
2104 pos < 0: get element from top of the stack */
2108 int min_inventory_size = -pos;
2109 int inventory_pos = player->inventory_size - min_inventory_size;
2110 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2112 return (player->inventory_size >= min_inventory_size ?
2113 player->inventory_element[inventory_pos] :
2114 player->inventory_infinite_element != EL_UNDEFINED ?
2115 player->inventory_infinite_element :
2116 player->dynabombs_left >= min_dynabombs_left ?
2117 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2122 int min_dynabombs_left = pos + 1;
2123 int min_inventory_size = pos + 1 - player->dynabombs_left;
2124 int inventory_pos = pos - player->dynabombs_left;
2126 return (player->inventory_infinite_element != EL_UNDEFINED ?
2127 player->inventory_infinite_element :
2128 player->dynabombs_left >= min_dynabombs_left ?
2129 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2130 player->inventory_size >= min_inventory_size ?
2131 player->inventory_element[inventory_pos] :
2136 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2138 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2139 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2142 if (gpo1->sort_priority != gpo2->sort_priority)
2143 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2145 compare_result = gpo1->nr - gpo2->nr;
2147 return compare_result;
2150 void InitGameControlValues()
2154 for (i = 0; game_panel_controls[i].nr != -1; i++)
2156 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2157 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2158 struct TextPosInfo *pos = gpc->pos;
2160 int type = gpc->type;
2164 Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2165 Error(ERR_EXIT, "this should not happen -- please debug");
2168 /* force update of game controls after initialization */
2169 gpc->value = gpc->last_value = -1;
2170 gpc->frame = gpc->last_frame = -1;
2171 gpc->gfx_frame = -1;
2173 /* determine panel value width for later calculation of alignment */
2174 if (type == TYPE_INTEGER || type == TYPE_STRING)
2176 pos->width = pos->size * getFontWidth(pos->font);
2177 pos->height = getFontHeight(pos->font);
2179 else if (type == TYPE_ELEMENT)
2181 pos->width = pos->size;
2182 pos->height = pos->size;
2185 /* fill structure for game panel draw order */
2187 gpo->sort_priority = pos->sort_priority;
2190 /* sort game panel controls according to sort_priority and control number */
2191 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2192 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2195 void UpdatePlayfieldElementCount()
2197 boolean use_element_count = FALSE;
2200 /* first check if it is needed at all to calculate playfield element count */
2201 for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2202 if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2203 use_element_count = TRUE;
2205 if (!use_element_count)
2208 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2209 element_info[i].element_count = 0;
2211 SCAN_PLAYFIELD(x, y)
2213 element_info[Feld[x][y]].element_count++;
2216 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2217 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2218 if (IS_IN_GROUP(j, i))
2219 element_info[EL_GROUP_START + i].element_count +=
2220 element_info[j].element_count;
2223 void UpdateGameControlValues()
2226 int time = (local_player->LevelSolved ?
2227 local_player->LevelSolved_CountingTime :
2228 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2229 level.native_em_level->lev->time :
2230 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2231 level.native_sp_level->game_sp->time_played :
2232 level.time == 0 ? TimePlayed : TimeLeft);
2233 int score = (local_player->LevelSolved ?
2234 local_player->LevelSolved_CountingScore :
2235 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2236 level.native_em_level->lev->score :
2237 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2238 level.native_sp_level->game_sp->score :
2239 local_player->score);
2240 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2241 level.native_em_level->lev->required :
2242 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2243 level.native_sp_level->game_sp->infotrons_still_needed :
2244 local_player->gems_still_needed);
2245 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2246 level.native_em_level->lev->required > 0 :
2247 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2248 level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2249 local_player->gems_still_needed > 0 ||
2250 local_player->sokobanfields_still_needed > 0 ||
2251 local_player->lights_still_needed > 0);
2253 UpdatePlayfieldElementCount();
2255 /* update game panel control values */
2257 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2258 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2260 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2261 for (i = 0; i < MAX_NUM_KEYS; i++)
2262 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2263 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2264 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2266 if (game.centered_player_nr == -1)
2268 for (i = 0; i < MAX_PLAYERS; i++)
2270 /* only one player in Supaplex game engine */
2271 if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2274 for (k = 0; k < MAX_NUM_KEYS; k++)
2276 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2278 if (level.native_em_level->ply[i]->keys & (1 << k))
2279 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2280 get_key_element_from_nr(k);
2282 else if (stored_player[i].key[k])
2283 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2284 get_key_element_from_nr(k);
2287 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2288 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2289 level.native_em_level->ply[i]->dynamite;
2290 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2291 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2292 level.native_sp_level->game_sp->red_disk_count;
2294 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2295 stored_player[i].inventory_size;
2297 if (stored_player[i].num_white_keys > 0)
2298 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2301 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2302 stored_player[i].num_white_keys;
2307 int player_nr = game.centered_player_nr;
2309 for (k = 0; k < MAX_NUM_KEYS; k++)
2311 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2313 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2314 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2315 get_key_element_from_nr(k);
2317 else if (stored_player[player_nr].key[k])
2318 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2319 get_key_element_from_nr(k);
2322 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2323 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2324 level.native_em_level->ply[player_nr]->dynamite;
2325 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2326 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2327 level.native_sp_level->game_sp->red_disk_count;
2329 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2330 stored_player[player_nr].inventory_size;
2332 if (stored_player[player_nr].num_white_keys > 0)
2333 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2335 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2336 stored_player[player_nr].num_white_keys;
2339 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2341 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2342 get_inventory_element_from_pos(local_player, i);
2343 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2344 get_inventory_element_from_pos(local_player, -i - 1);
2347 game_panel_controls[GAME_PANEL_SCORE].value = score;
2348 game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2350 game_panel_controls[GAME_PANEL_TIME].value = time;
2352 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2353 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2354 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2356 game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2358 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2359 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2361 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2362 local_player->shield_normal_time_left;
2363 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2364 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2366 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2367 local_player->shield_deadly_time_left;
2369 game_panel_controls[GAME_PANEL_EXIT].value =
2370 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2372 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2373 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2374 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2375 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2376 EL_EMC_MAGIC_BALL_SWITCH);
2378 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2379 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2380 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2381 game.light_time_left;
2383 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2384 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2385 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2386 game.timegate_time_left;
2388 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2389 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2391 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2392 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2393 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2394 game.lenses_time_left;
2396 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2397 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2398 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2399 game.magnify_time_left;
2401 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2402 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2403 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2404 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2405 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2406 EL_BALLOON_SWITCH_NONE);
2408 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2409 local_player->dynabomb_count;
2410 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2411 local_player->dynabomb_size;
2412 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2413 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2415 game_panel_controls[GAME_PANEL_PENGUINS].value =
2416 local_player->friends_still_needed;
2418 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2419 local_player->sokobanfields_still_needed;
2420 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2421 local_player->sokobanfields_still_needed;
2423 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2424 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2426 for (i = 0; i < NUM_BELTS; i++)
2428 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2429 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2430 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2431 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2432 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2435 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2436 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2437 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2438 game.magic_wall_time_left;
2440 #if USE_PLAYER_GRAVITY
2441 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2442 local_player->gravity;
2444 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2447 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2448 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2450 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2451 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2452 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2453 game.panel.element[i].id : EL_UNDEFINED);
2455 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2456 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2457 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2458 element_info[game.panel.element_count[i].id].element_count : 0);
2460 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2461 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2462 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2463 element_info[game.panel.ce_score[i].id].collect_score : 0);
2465 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2466 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2467 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2468 element_info[game.panel.ce_score_element[i].id].collect_score :
2471 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2472 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2473 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2475 /* update game panel control frames */
2477 for (i = 0; game_panel_controls[i].nr != -1; i++)
2479 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2481 if (gpc->type == TYPE_ELEMENT)
2483 if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2485 int last_anim_random_frame = gfx.anim_random_frame;
2486 int element = gpc->value;
2487 int graphic = el2panelimg(element);
2489 if (gpc->value != gpc->last_value)
2492 gpc->gfx_random = INIT_GFX_RANDOM();
2498 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2499 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2500 gpc->gfx_random = INIT_GFX_RANDOM();
2503 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2504 gfx.anim_random_frame = gpc->gfx_random;
2506 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2507 gpc->gfx_frame = element_info[element].collect_score;
2509 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2512 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2513 gfx.anim_random_frame = last_anim_random_frame;
2519 void DisplayGameControlValues()
2521 boolean redraw_panel = FALSE;
2524 for (i = 0; game_panel_controls[i].nr != -1; i++)
2526 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2528 if (PANEL_DEACTIVATED(gpc->pos))
2531 if (gpc->value == gpc->last_value &&
2532 gpc->frame == gpc->last_frame)
2535 redraw_panel = TRUE;
2541 /* copy default game door content to main double buffer */
2543 /* !!! CHECK AGAIN !!! */
2544 SetPanelBackground();
2545 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2546 DrawBackground(DX, DY, DXSIZE, DYSIZE);
2548 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2549 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2552 /* redraw game control buttons */
2554 RedrawGameButtons();
2560 game_status = GAME_MODE_PSEUDO_PANEL;
2563 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2565 for (i = 0; game_panel_controls[i].nr != -1; i++)
2569 int nr = game_panel_order[i].nr;
2570 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2572 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2575 struct TextPosInfo *pos = gpc->pos;
2576 int type = gpc->type;
2577 int value = gpc->value;
2578 int frame = gpc->frame;
2580 int last_value = gpc->last_value;
2581 int last_frame = gpc->last_frame;
2583 int size = pos->size;
2584 int font = pos->font;
2585 boolean draw_masked = pos->draw_masked;
2586 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2588 if (PANEL_DEACTIVATED(pos))
2592 if (value == last_value && frame == last_frame)
2596 gpc->last_value = value;
2597 gpc->last_frame = frame;
2600 printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2603 if (type == TYPE_INTEGER)
2605 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2606 nr == GAME_PANEL_TIME)
2608 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2610 if (use_dynamic_size) /* use dynamic number of digits */
2612 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2613 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2614 int size2 = size1 + 1;
2615 int font1 = pos->font;
2616 int font2 = pos->font_alt;
2618 size = (value < value_change ? size1 : size2);
2619 font = (value < value_change ? font1 : font2);
2622 /* clear background if value just changed its size (dynamic digits) */
2623 if ((last_value < value_change) != (value < value_change))
2625 int width1 = size1 * getFontWidth(font1);
2626 int width2 = size2 * getFontWidth(font2);
2627 int max_width = MAX(width1, width2);
2628 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2630 pos->width = max_width;
2632 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2633 max_width, max_height);
2640 /* correct text size if "digits" is zero or less */
2642 size = strlen(int2str(value, size));
2644 /* dynamically correct text alignment */
2645 pos->width = size * getFontWidth(font);
2648 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2649 int2str(value, size), font, mask_mode);
2651 else if (type == TYPE_ELEMENT)
2653 int element, graphic;
2657 int dst_x = PANEL_XPOS(pos);
2658 int dst_y = PANEL_YPOS(pos);
2661 if (value != EL_UNDEFINED && value != EL_EMPTY)
2664 graphic = el2panelimg(value);
2666 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2669 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2673 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2676 width = graphic_info[graphic].width * size / TILESIZE;
2677 height = graphic_info[graphic].height * size / TILESIZE;
2681 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2682 dst_x - src_x, dst_y - src_y);
2683 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2688 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2693 if (value == EL_UNDEFINED || value == EL_EMPTY)
2695 element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2696 graphic = el2panelimg(element);
2698 src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2699 src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2700 src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2705 graphic = el2panelimg(value);
2707 getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2710 width = graphic_info[graphic].width * size / TILESIZE;
2711 height = graphic_info[graphic].height * size / TILESIZE;
2713 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2716 else if (type == TYPE_STRING)
2718 boolean active = (value != 0);
2719 char *state_normal = "off";
2720 char *state_active = "on";
2721 char *state = (active ? state_active : state_normal);
2722 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2723 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2724 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2725 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2727 if (nr == GAME_PANEL_GRAVITY_STATE)
2729 int font1 = pos->font; /* (used for normal state) */
2730 int font2 = pos->font_alt; /* (used for active state) */
2732 int size1 = strlen(state_normal);
2733 int size2 = strlen(state_active);
2734 int width1 = size1 * getFontWidth(font1);
2735 int width2 = size2 * getFontWidth(font2);
2736 int max_width = MAX(width1, width2);
2737 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2739 pos->width = max_width;
2741 /* clear background for values that may have changed its size */
2742 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2743 max_width, max_height);
2746 font = (active ? font2 : font1);
2756 /* don't truncate output if "chars" is zero or less */
2759 /* dynamically correct text alignment */
2760 pos->width = size * getFontWidth(font);
2764 s_cut = getStringCopyN(s, size);
2766 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2767 s_cut, font, mask_mode);
2773 redraw_mask |= REDRAW_DOOR_1;
2776 game_status = GAME_MODE_PLAYING;
2779 void UpdateAndDisplayGameControlValues()
2781 if (tape.warp_forward)
2784 UpdateGameControlValues();
2785 DisplayGameControlValues();
2788 void DrawGameValue_Emeralds(int value)
2790 struct TextPosInfo *pos = &game.panel.gems;
2792 int font_nr = pos->font;
2794 int font_nr = FONT_TEXT_2;
2796 int font_width = getFontWidth(font_nr);
2797 int chars = pos->size;
2800 return; /* !!! USE NEW STUFF !!! */
2803 if (PANEL_DEACTIVATED(pos))
2806 pos->width = chars * font_width;
2808 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2811 void DrawGameValue_Dynamite(int value)
2813 struct TextPosInfo *pos = &game.panel.inventory_count;
2815 int font_nr = pos->font;
2817 int font_nr = FONT_TEXT_2;
2819 int font_width = getFontWidth(font_nr);
2820 int chars = pos->size;
2823 return; /* !!! USE NEW STUFF !!! */
2826 if (PANEL_DEACTIVATED(pos))
2829 pos->width = chars * font_width;
2831 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2834 void DrawGameValue_Score(int value)
2836 struct TextPosInfo *pos = &game.panel.score;
2838 int font_nr = pos->font;
2840 int font_nr = FONT_TEXT_2;
2842 int font_width = getFontWidth(font_nr);
2843 int chars = pos->size;
2846 return; /* !!! USE NEW STUFF !!! */
2849 if (PANEL_DEACTIVATED(pos))
2852 pos->width = chars * font_width;
2854 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2857 void DrawGameValue_Time(int value)
2859 struct TextPosInfo *pos = &game.panel.time;
2860 static int last_value = -1;
2863 int chars = pos->size;
2865 int font1_nr = pos->font;
2866 int font2_nr = pos->font_alt;
2868 int font1_nr = FONT_TEXT_2;
2869 int font2_nr = FONT_TEXT_1;
2871 int font_nr = font1_nr;
2872 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2875 return; /* !!! USE NEW STUFF !!! */
2878 if (PANEL_DEACTIVATED(pos))
2881 if (use_dynamic_chars) /* use dynamic number of chars */
2883 chars = (value < 1000 ? chars1 : chars2);
2884 font_nr = (value < 1000 ? font1_nr : font2_nr);
2887 /* clear background if value just changed its size (dynamic chars only) */
2888 if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2890 int width1 = chars1 * getFontWidth(font1_nr);
2891 int width2 = chars2 * getFontWidth(font2_nr);
2892 int max_width = MAX(width1, width2);
2893 int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2895 pos->width = max_width;
2897 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2898 max_width, max_height);
2901 pos->width = chars * getFontWidth(font_nr);
2903 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2908 void DrawGameValue_Level(int value)
2910 struct TextPosInfo *pos = &game.panel.level_number;
2913 int chars = pos->size;
2915 int font1_nr = pos->font;
2916 int font2_nr = pos->font_alt;
2918 int font1_nr = FONT_TEXT_2;
2919 int font2_nr = FONT_TEXT_1;
2921 int font_nr = font1_nr;
2922 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2925 return; /* !!! USE NEW STUFF !!! */
2928 if (PANEL_DEACTIVATED(pos))
2931 if (use_dynamic_chars) /* use dynamic number of chars */
2933 chars = (level_nr < 100 ? chars1 : chars2);
2934 font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2937 pos->width = chars * getFontWidth(font_nr);
2939 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2942 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2945 struct TextPosInfo *pos = &game.panel.keys;
2948 int base_key_graphic = EL_KEY_1;
2953 return; /* !!! USE NEW STUFF !!! */
2957 if (PANEL_DEACTIVATED(pos))
2962 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2963 base_key_graphic = EL_EM_KEY_1;
2967 pos->width = 4 * MINI_TILEX;
2971 for (i = 0; i < MAX_NUM_KEYS; i++)
2973 /* currently only 4 of 8 possible keys are displayed */
2974 for (i = 0; i < STD_NUM_KEYS; i++)
2978 struct TextPosInfo *pos = &game.panel.key[i];
2980 int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2981 int src_y = DOOR_GFX_PAGEY1 + 123;
2983 int dst_x = PANEL_XPOS(pos);
2984 int dst_y = PANEL_YPOS(pos);
2986 int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2987 int dst_y = PANEL_YPOS(pos);
2991 int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2992 level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2994 int graphic = el2edimg(element);
2998 if (PANEL_DEACTIVATED(pos))
3003 /* masked blit with tiles from half-size scaled bitmap does not work yet
3004 (no mask bitmap created for these sizes after loading and scaling) --
3005 solution: load without creating mask, scale, then create final mask */
3007 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3008 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3013 int graphic = el2edimg(base_key_graphic + i);
3018 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
3020 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
3021 dst_x - src_x, dst_y - src_y);
3022 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
3028 DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
3030 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3031 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3034 DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
3036 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3037 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3045 void DrawGameValue_Emeralds(int value)
3047 int font_nr = FONT_TEXT_2;
3048 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3050 if (PANEL_DEACTIVATED(game.panel.gems))
3053 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
3056 void DrawGameValue_Dynamite(int value)
3058 int font_nr = FONT_TEXT_2;
3059 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3061 if (PANEL_DEACTIVATED(game.panel.inventory_count))
3064 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
3067 void DrawGameValue_Score(int value)
3069 int font_nr = FONT_TEXT_2;
3070 int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
3072 if (PANEL_DEACTIVATED(game.panel.score))
3075 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
3078 void DrawGameValue_Time(int value)
3080 int font1_nr = FONT_TEXT_2;
3082 int font2_nr = FONT_TEXT_1;
3084 int font2_nr = FONT_LEVEL_NUMBER;
3086 int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
3087 int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
3089 if (PANEL_DEACTIVATED(game.panel.time))
3092 /* clear background if value just changed its size */
3093 if (value == 999 || value == 1000)
3094 ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
3097 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
3099 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
3102 void DrawGameValue_Level(int value)
3104 int font1_nr = FONT_TEXT_2;
3106 int font2_nr = FONT_TEXT_1;
3108 int font2_nr = FONT_LEVEL_NUMBER;
3111 if (PANEL_DEACTIVATED(game.panel.level))
3115 DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
3117 DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
3120 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
3122 int base_key_graphic = EL_KEY_1;
3125 if (PANEL_DEACTIVATED(game.panel.keys))
3128 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3129 base_key_graphic = EL_EM_KEY_1;
3131 /* currently only 4 of 8 possible keys are displayed */
3132 for (i = 0; i < STD_NUM_KEYS; i++)
3134 int x = XX_KEYS + i * MINI_TILEX;
3138 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3140 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3141 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3147 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3150 int key[MAX_NUM_KEYS];
3153 /* prevent EM engine from updating time/score values parallel to GameWon() */
3154 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3155 local_player->LevelSolved)
3158 for (i = 0; i < MAX_NUM_KEYS; i++)
3159 key[i] = key_bits & (1 << i);
3161 DrawGameValue_Level(level_nr);
3163 DrawGameValue_Emeralds(emeralds);
3164 DrawGameValue_Dynamite(dynamite);
3165 DrawGameValue_Score(score);
3166 DrawGameValue_Time(time);
3168 DrawGameValue_Keys(key);
3171 void UpdateGameDoorValues()
3173 UpdateGameControlValues();
3176 void DrawGameDoorValues()
3178 DisplayGameControlValues();
3181 void DrawGameDoorValues_OLD()
3183 int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
3184 int dynamite_value = 0;
3185 int score_value = (local_player->LevelSolved ? local_player->score_final :
3186 local_player->score);
3187 int gems_value = local_player->gems_still_needed;
3191 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3193 DrawGameDoorValues_EM();
3198 if (game.centered_player_nr == -1)
3200 for (i = 0; i < MAX_PLAYERS; i++)
3202 for (j = 0; j < MAX_NUM_KEYS; j++)
3203 if (stored_player[i].key[j])
3204 key_bits |= (1 << j);
3206 dynamite_value += stored_player[i].inventory_size;
3211 int player_nr = game.centered_player_nr;
3213 for (i = 0; i < MAX_NUM_KEYS; i++)
3214 if (stored_player[player_nr].key[i])
3215 key_bits |= (1 << i);
3217 dynamite_value = stored_player[player_nr].inventory_size;
3220 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3226 =============================================================================
3228 -----------------------------------------------------------------------------
3229 initialize game engine due to level / tape version number
3230 =============================================================================
3233 static void InitGameEngine()
3235 int i, j, k, l, x, y;
3237 /* set game engine from tape file when re-playing, else from level file */
3238 game.engine_version = (tape.playing ? tape.engine_version :
3239 level.game_version);
3241 /* ---------------------------------------------------------------------- */
3242 /* set flags for bugs and changes according to active game engine version */
3243 /* ---------------------------------------------------------------------- */
3246 Summary of bugfix/change:
3247 Fixed handling for custom elements that change when pushed by the player.
3249 Fixed/changed in version:
3253 Before 3.1.0, custom elements that "change when pushing" changed directly
3254 after the player started pushing them (until then handled in "DigField()").
3255 Since 3.1.0, these custom elements are not changed until the "pushing"
3256 move of the element is finished (now handled in "ContinueMoving()").
3258 Affected levels/tapes:
3259 The first condition is generally needed for all levels/tapes before version
3260 3.1.0, which might use the old behaviour before it was changed; known tapes
3261 that are affected are some tapes from the level set "Walpurgis Gardens" by
3263 The second condition is an exception from the above case and is needed for
3264 the special case of tapes recorded with game (not engine!) version 3.1.0 or
3265 above (including some development versions of 3.1.0), but before it was
3266 known that this change would break tapes like the above and was fixed in
3267 3.1.1, so that the changed behaviour was active although the engine version
3268 while recording maybe was before 3.1.0. There is at least one tape that is
3269 affected by this exception, which is the tape for the one-level set "Bug
3270 Machine" by Juergen Bonhagen.
3273 game.use_change_when_pushing_bug =
3274 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3276 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3277 tape.game_version < VERSION_IDENT(3,1,1,0)));
3280 Summary of bugfix/change:
3281 Fixed handling for blocking the field the player leaves when moving.
3283 Fixed/changed in version:
3287 Before 3.1.1, when "block last field when moving" was enabled, the field
3288 the player is leaving when moving was blocked for the time of the move,
3289 and was directly unblocked afterwards. This resulted in the last field
3290 being blocked for exactly one less than the number of frames of one player
3291 move. Additionally, even when blocking was disabled, the last field was
3292 blocked for exactly one frame.
3293 Since 3.1.1, due to changes in player movement handling, the last field
3294 is not blocked at all when blocking is disabled. When blocking is enabled,
3295 the last field is blocked for exactly the number of frames of one player
3296 move. Additionally, if the player is Murphy, the hero of Supaplex, the
3297 last field is blocked for exactly one more than the number of frames of
3300 Affected levels/tapes:
3301 (!!! yet to be determined -- probably many !!!)
3304 game.use_block_last_field_bug =
3305 (game.engine_version < VERSION_IDENT(3,1,1,0));
3308 Summary of bugfix/change:
3309 Changed behaviour of CE changes with multiple changes per single frame.
3311 Fixed/changed in version:
3315 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3316 This resulted in race conditions where CEs seem to behave strange in some
3317 situations (where triggered CE changes were just skipped because there was
3318 already a CE change on that tile in the playfield in that engine frame).
3319 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3320 (The number of changes per frame must be limited in any case, because else
3321 it is easily possible to define CE changes that would result in an infinite
3322 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3323 should be set large enough so that it would only be reached in cases where
3324 the corresponding CE change conditions run into a loop. Therefore, it seems
3325 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3326 maximal number of change pages for custom elements.)
3328 Affected levels/tapes:
3332 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3333 game.max_num_changes_per_frame = 1;
3335 game.max_num_changes_per_frame =
3336 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3339 /* ---------------------------------------------------------------------- */
3341 /* default scan direction: scan playfield from top/left to bottom/right */
3342 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3344 /* dynamically adjust element properties according to game engine version */
3345 InitElementPropertiesEngine(game.engine_version);
3348 printf("level %d: level version == %06d\n", level_nr, level.game_version);
3349 printf(" tape version == %06d [%s] [file: %06d]\n",
3350 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3352 printf(" => game.engine_version == %06d\n", game.engine_version);
3355 /* ---------- initialize player's initial move delay --------------------- */
3357 /* dynamically adjust player properties according to level information */
3358 for (i = 0; i < MAX_PLAYERS; i++)
3359 game.initial_move_delay_value[i] =
3360 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3362 /* dynamically adjust player properties according to game engine version */
3363 for (i = 0; i < MAX_PLAYERS; i++)
3364 game.initial_move_delay[i] =
3365 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3366 game.initial_move_delay_value[i] : 0);
3368 /* ---------- initialize player's initial push delay --------------------- */
3370 /* dynamically adjust player properties according to game engine version */
3371 game.initial_push_delay_value =
3372 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3374 /* ---------- initialize changing elements ------------------------------- */
3376 /* initialize changing elements information */
3377 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3379 struct ElementInfo *ei = &element_info[i];
3381 /* this pointer might have been changed in the level editor */
3382 ei->change = &ei->change_page[0];
3384 if (!IS_CUSTOM_ELEMENT(i))
3386 ei->change->target_element = EL_EMPTY_SPACE;
3387 ei->change->delay_fixed = 0;
3388 ei->change->delay_random = 0;
3389 ei->change->delay_frames = 1;
3392 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3394 ei->has_change_event[j] = FALSE;
3396 ei->event_page_nr[j] = 0;
3397 ei->event_page[j] = &ei->change_page[0];
3401 /* add changing elements from pre-defined list */
3402 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3404 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3405 struct ElementInfo *ei = &element_info[ch_delay->element];
3407 ei->change->target_element = ch_delay->target_element;
3408 ei->change->delay_fixed = ch_delay->change_delay;
3410 ei->change->pre_change_function = ch_delay->pre_change_function;
3411 ei->change->change_function = ch_delay->change_function;
3412 ei->change->post_change_function = ch_delay->post_change_function;
3414 ei->change->can_change = TRUE;
3415 ei->change->can_change_or_has_action = TRUE;
3417 ei->has_change_event[CE_DELAY] = TRUE;
3419 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3420 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3423 /* ---------- initialize internal run-time variables --------------------- */
3425 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3427 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3429 for (j = 0; j < ei->num_change_pages; j++)
3431 ei->change_page[j].can_change_or_has_action =
3432 (ei->change_page[j].can_change |
3433 ei->change_page[j].has_action);
3437 /* add change events from custom element configuration */
3438 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3440 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3442 for (j = 0; j < ei->num_change_pages; j++)
3444 if (!ei->change_page[j].can_change_or_has_action)
3447 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3449 /* only add event page for the first page found with this event */
3450 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3452 ei->has_change_event[k] = TRUE;
3454 ei->event_page_nr[k] = j;
3455 ei->event_page[k] = &ei->change_page[j];
3462 /* ---------- initialize reference elements in change conditions --------- */
3464 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3466 int element = EL_CUSTOM_START + i;
3467 struct ElementInfo *ei = &element_info[element];
3469 for (j = 0; j < ei->num_change_pages; j++)
3471 int trigger_element = ei->change_page[j].initial_trigger_element;
3473 if (trigger_element >= EL_PREV_CE_8 &&
3474 trigger_element <= EL_NEXT_CE_8)
3475 trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3477 ei->change_page[j].trigger_element = trigger_element;
3482 /* ---------- initialize run-time trigger player and element ------------- */
3484 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3486 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3488 for (j = 0; j < ei->num_change_pages; j++)
3490 ei->change_page[j].actual_trigger_element = EL_EMPTY;
3491 ei->change_page[j].actual_trigger_player = EL_EMPTY;
3492 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3493 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3494 ei->change_page[j].actual_trigger_ce_value = 0;
3495 ei->change_page[j].actual_trigger_ce_score = 0;
3499 /* ---------- initialize trigger events ---------------------------------- */
3501 /* initialize trigger events information */
3502 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3503 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3504 trigger_events[i][j] = FALSE;
3506 /* add trigger events from element change event properties */
3507 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3509 struct ElementInfo *ei = &element_info[i];
3511 for (j = 0; j < ei->num_change_pages; j++)
3513 if (!ei->change_page[j].can_change_or_has_action)
3516 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3518 int trigger_element = ei->change_page[j].trigger_element;
3520 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3522 if (ei->change_page[j].has_event[k])
3524 if (IS_GROUP_ELEMENT(trigger_element))
3526 struct ElementGroupInfo *group =
3527 element_info[trigger_element].group;
3529 for (l = 0; l < group->num_elements_resolved; l++)
3530 trigger_events[group->element_resolved[l]][k] = TRUE;
3532 else if (trigger_element == EL_ANY_ELEMENT)
3533 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3534 trigger_events[l][k] = TRUE;
3536 trigger_events[trigger_element][k] = TRUE;
3543 /* ---------- initialize push delay -------------------------------------- */
3545 /* initialize push delay values to default */
3546 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3548 if (!IS_CUSTOM_ELEMENT(i))
3550 /* set default push delay values (corrected since version 3.0.7-1) */
3551 if (game.engine_version < VERSION_IDENT(3,0,7,1))
3553 element_info[i].push_delay_fixed = 2;
3554 element_info[i].push_delay_random = 8;
3558 element_info[i].push_delay_fixed = 8;
3559 element_info[i].push_delay_random = 8;
3564 /* set push delay value for certain elements from pre-defined list */
3565 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3567 int e = push_delay_list[i].element;
3569 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
3570 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3573 /* set push delay value for Supaplex elements for newer engine versions */
3574 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3576 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3578 if (IS_SP_ELEMENT(i))
3580 /* set SP push delay to just enough to push under a falling zonk */
3581 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3583 element_info[i].push_delay_fixed = delay;
3584 element_info[i].push_delay_random = 0;
3589 /* ---------- initialize move stepsize ----------------------------------- */
3591 /* initialize move stepsize values to default */
3592 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3593 if (!IS_CUSTOM_ELEMENT(i))
3594 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3596 /* set move stepsize value for certain elements from pre-defined list */
3597 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3599 int e = move_stepsize_list[i].element;
3601 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3604 /* ---------- initialize collect score ----------------------------------- */
3606 /* initialize collect score values for custom elements from initial value */
3607 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3608 if (IS_CUSTOM_ELEMENT(i))
3609 element_info[i].collect_score = element_info[i].collect_score_initial;
3611 /* ---------- initialize collect count ----------------------------------- */
3613 /* initialize collect count values for non-custom elements */
3614 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3615 if (!IS_CUSTOM_ELEMENT(i))
3616 element_info[i].collect_count_initial = 0;
3618 /* add collect count values for all elements from pre-defined list */
3619 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3620 element_info[collect_count_list[i].element].collect_count_initial =
3621 collect_count_list[i].count;
3623 /* ---------- initialize access direction -------------------------------- */
3625 /* initialize access direction values to default (access from every side) */
3626 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3627 if (!IS_CUSTOM_ELEMENT(i))
3628 element_info[i].access_direction = MV_ALL_DIRECTIONS;
3630 /* set access direction value for certain elements from pre-defined list */
3631 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3632 element_info[access_direction_list[i].element].access_direction =
3633 access_direction_list[i].direction;
3635 /* ---------- initialize explosion content ------------------------------- */
3636 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3638 if (IS_CUSTOM_ELEMENT(i))
3641 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3643 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3645 element_info[i].content.e[x][y] =
3646 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3647 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3648 i == EL_PLAYER_3 ? EL_EMERALD :
3649 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3650 i == EL_MOLE ? EL_EMERALD_RED :
3651 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3652 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3653 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3654 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3655 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3656 i == EL_WALL_EMERALD ? EL_EMERALD :
3657 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3658 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3659 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3660 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3661 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3662 i == EL_WALL_PEARL ? EL_PEARL :
3663 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3668 /* ---------- initialize recursion detection ------------------------------ */
3669 recursion_loop_depth = 0;
3670 recursion_loop_detected = FALSE;
3671 recursion_loop_element = EL_UNDEFINED;
3673 /* ---------- initialize graphics engine ---------------------------------- */
3674 game.scroll_delay_value =
3675 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3676 setup.scroll_delay ? setup.scroll_delay_value : 0);
3677 game.scroll_delay_value =
3678 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3681 int get_num_special_action(int element, int action_first, int action_last)
3683 int num_special_action = 0;
3686 for (i = action_first; i <= action_last; i++)
3688 boolean found = FALSE;
3690 for (j = 0; j < NUM_DIRECTIONS; j++)
3691 if (el_act_dir2img(element, i, j) !=
3692 el_act_dir2img(element, ACTION_DEFAULT, j))
3696 num_special_action++;
3701 return num_special_action;
3706 =============================================================================
3708 -----------------------------------------------------------------------------
3709 initialize and start new game
3710 =============================================================================
3715 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3716 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3717 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3719 boolean do_fading = (game_status == GAME_MODE_MAIN);
3722 int initial_move_dir = MV_DOWN;
3724 int initial_move_dir = MV_NONE;
3728 game_status = GAME_MODE_PLAYING;
3731 /* needed if different viewport properties defined for playing */
3732 ChangeViewportPropertiesIfNeeded();
3736 DrawCompleteVideoDisplay();
3740 InitGameControlValues();
3742 /* don't play tapes over network */
3743 network_playing = (options.network && !tape.playing);
3745 for (i = 0; i < MAX_PLAYERS; i++)
3747 struct PlayerInfo *player = &stored_player[i];
3749 player->index_nr = i;
3750 player->index_bit = (1 << i);
3751 player->element_nr = EL_PLAYER_1 + i;
3753 player->present = FALSE;
3754 player->active = FALSE;
3755 player->mapped = FALSE;
3757 player->killed = FALSE;
3758 player->reanimated = FALSE;
3761 player->effective_action = 0;
3762 player->programmed_action = 0;
3765 player->score_final = 0;
3767 player->gems_still_needed = level.gems_needed;
3768 player->sokobanfields_still_needed = 0;
3769 player->lights_still_needed = 0;
3770 player->friends_still_needed = 0;
3772 for (j = 0; j < MAX_NUM_KEYS; j++)
3773 player->key[j] = FALSE;
3775 player->num_white_keys = 0;
3777 player->dynabomb_count = 0;
3778 player->dynabomb_size = 1;
3779 player->dynabombs_left = 0;
3780 player->dynabomb_xl = FALSE;
3782 player->MovDir = initial_move_dir;
3785 player->GfxDir = initial_move_dir;
3786 player->GfxAction = ACTION_DEFAULT;
3788 player->StepFrame = 0;
3790 player->initial_element = player->element_nr;
3791 player->artwork_element =
3792 (level.use_artwork_element[i] ? level.artwork_element[i] :
3793 player->element_nr);
3794 player->use_murphy = FALSE;
3796 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3797 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3799 player->gravity = level.initial_player_gravity[i];
3801 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3803 player->actual_frame_counter = 0;
3805 player->step_counter = 0;
3807 player->last_move_dir = initial_move_dir;
3809 player->is_active = FALSE;
3811 player->is_waiting = FALSE;
3812 player->is_moving = FALSE;
3813 player->is_auto_moving = FALSE;
3814 player->is_digging = FALSE;
3815 player->is_snapping = FALSE;
3816 player->is_collecting = FALSE;
3817 player->is_pushing = FALSE;
3818 player->is_switching = FALSE;
3819 player->is_dropping = FALSE;
3820 player->is_dropping_pressed = FALSE;
3822 player->is_bored = FALSE;
3823 player->is_sleeping = FALSE;
3825 player->frame_counter_bored = -1;
3826 player->frame_counter_sleeping = -1;
3828 player->anim_delay_counter = 0;
3829 player->post_delay_counter = 0;
3831 player->dir_waiting = initial_move_dir;
3832 player->action_waiting = ACTION_DEFAULT;
3833 player->last_action_waiting = ACTION_DEFAULT;
3834 player->special_action_bored = ACTION_DEFAULT;
3835 player->special_action_sleeping = ACTION_DEFAULT;
3837 player->switch_x = -1;
3838 player->switch_y = -1;
3840 player->drop_x = -1;
3841 player->drop_y = -1;
3843 player->show_envelope = 0;
3845 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3847 player->push_delay = -1; /* initialized when pushing starts */
3848 player->push_delay_value = game.initial_push_delay_value;
3850 player->drop_delay = 0;
3851 player->drop_pressed_delay = 0;
3853 player->last_jx = -1;
3854 player->last_jy = -1;
3858 player->shield_normal_time_left = 0;
3859 player->shield_deadly_time_left = 0;
3861 player->inventory_infinite_element = EL_UNDEFINED;
3862 player->inventory_size = 0;
3864 if (level.use_initial_inventory[i])
3866 for (j = 0; j < level.initial_inventory_size[i]; j++)
3868 int element = level.initial_inventory_content[i][j];
3869 int collect_count = element_info[element].collect_count_initial;
3872 if (!IS_CUSTOM_ELEMENT(element))
3875 if (collect_count == 0)
3876 player->inventory_infinite_element = element;
3878 for (k = 0; k < collect_count; k++)
3879 if (player->inventory_size < MAX_INVENTORY_SIZE)
3880 player->inventory_element[player->inventory_size++] = element;
3884 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3885 SnapField(player, 0, 0);
3887 player->LevelSolved = FALSE;
3888 player->GameOver = FALSE;
3890 player->LevelSolved_GameWon = FALSE;
3891 player->LevelSolved_GameEnd = FALSE;
3892 player->LevelSolved_PanelOff = FALSE;
3893 player->LevelSolved_SaveTape = FALSE;
3894 player->LevelSolved_SaveScore = FALSE;
3895 player->LevelSolved_CountingTime = 0;
3896 player->LevelSolved_CountingScore = 0;
3898 map_player_action[i] = i;
3901 network_player_action_received = FALSE;
3903 #if defined(NETWORK_AVALIABLE)
3904 /* initial null action */
3905 if (network_playing)
3906 SendToServer_MovePlayer(MV_NONE);
3915 TimeLeft = level.time;
3918 ScreenMovDir = MV_NONE;
3922 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3924 AllPlayersGone = FALSE;
3926 game.yamyam_content_nr = 0;
3927 game.robot_wheel_active = FALSE;
3928 game.magic_wall_active = FALSE;
3929 game.magic_wall_time_left = 0;
3930 game.light_time_left = 0;
3931 game.timegate_time_left = 0;
3932 game.switchgate_pos = 0;
3933 game.wind_direction = level.wind_direction_initial;
3935 #if !USE_PLAYER_GRAVITY
3936 game.gravity = FALSE;
3937 game.explosions_delayed = TRUE;
3940 game.lenses_time_left = 0;
3941 game.magnify_time_left = 0;
3943 game.ball_state = level.ball_state_initial;
3944 game.ball_content_nr = 0;
3946 game.envelope_active = FALSE;
3948 /* set focus to local player for network games, else to all players */
3949 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3950 game.centered_player_nr_next = game.centered_player_nr;
3951 game.set_centered_player = FALSE;
3953 if (network_playing && tape.recording)
3955 /* store client dependent player focus when recording network games */
3956 tape.centered_player_nr_next = game.centered_player_nr_next;
3957 tape.set_centered_player = TRUE;
3960 for (i = 0; i < NUM_BELTS; i++)
3962 game.belt_dir[i] = MV_NONE;
3963 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3966 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3967 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3969 SCAN_PLAYFIELD(x, y)
3971 Feld[x][y] = level.field[x][y];
3972 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3973 ChangeDelay[x][y] = 0;
3974 ChangePage[x][y] = -1;
3975 #if USE_NEW_CUSTOM_VALUE
3976 CustomValue[x][y] = 0; /* initialized in InitField() */
3978 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3980 WasJustMoving[x][y] = 0;
3981 WasJustFalling[x][y] = 0;
3982 CheckCollision[x][y] = 0;
3983 CheckImpact[x][y] = 0;
3985 Pushed[x][y] = FALSE;
3987 ChangeCount[x][y] = 0;
3988 ChangeEvent[x][y] = -1;
3990 ExplodePhase[x][y] = 0;
3991 ExplodeDelay[x][y] = 0;
3992 ExplodeField[x][y] = EX_TYPE_NONE;
3994 RunnerVisit[x][y] = 0;
3995 PlayerVisit[x][y] = 0;
3998 GfxRandom[x][y] = INIT_GFX_RANDOM();
3999 GfxElement[x][y] = EL_UNDEFINED;
4000 GfxAction[x][y] = ACTION_DEFAULT;
4001 GfxDir[x][y] = MV_NONE;
4002 GfxRedraw[x][y] = GFX_REDRAW_NONE;
4005 SCAN_PLAYFIELD(x, y)
4007 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
4009 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
4011 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
4014 InitField(x, y, TRUE);
4016 ResetGfxAnimation(x, y);
4021 for (i = 0; i < MAX_PLAYERS; i++)
4023 struct PlayerInfo *player = &stored_player[i];
4025 /* set number of special actions for bored and sleeping animation */
4026 player->num_special_action_bored =
4027 get_num_special_action(player->artwork_element,
4028 ACTION_BORING_1, ACTION_BORING_LAST);
4029 player->num_special_action_sleeping =
4030 get_num_special_action(player->artwork_element,
4031 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
4034 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
4035 emulate_sb ? EMU_SOKOBAN :
4036 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
4038 #if USE_NEW_ALL_SLIPPERY
4039 /* initialize type of slippery elements */
4040 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4042 if (!IS_CUSTOM_ELEMENT(i))
4044 /* default: elements slip down either to the left or right randomly */
4045 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
4047 /* SP style elements prefer to slip down on the left side */
4048 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
4049 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4051 /* BD style elements prefer to slip down on the left side */
4052 if (game.emulation == EMU_BOULDERDASH)
4053 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4058 /* initialize explosion and ignition delay */
4059 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4061 if (!IS_CUSTOM_ELEMENT(i))
4064 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4065 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4066 game.emulation == EMU_SUPAPLEX ? 3 : 2);
4067 int last_phase = (num_phase + 1) * delay;
4068 int half_phase = (num_phase / 2) * delay;
4070 element_info[i].explosion_delay = last_phase - 1;
4071 element_info[i].ignition_delay = half_phase;
4073 if (i == EL_BLACK_ORB)
4074 element_info[i].ignition_delay = 1;
4078 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
4079 element_info[i].explosion_delay = 1;
4081 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
4082 element_info[i].ignition_delay = 1;
4086 /* correct non-moving belts to start moving left */
4087 for (i = 0; i < NUM_BELTS; i++)
4088 if (game.belt_dir[i] == MV_NONE)
4089 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
4091 #if USE_NEW_PLAYER_ASSIGNMENTS
4092 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4093 /* choose default local player */
4094 local_player = &stored_player[0];
4096 for (i = 0; i < MAX_PLAYERS; i++)
4097 stored_player[i].connected = FALSE;
4099 local_player->connected = TRUE;
4100 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4104 /* try to guess locally connected team mode players (needed for correct
4105 assignment of player figures from level to locally playing players) */
4107 for (i = 0; i < MAX_PLAYERS; i++)
4108 if (tape.player_participates[i])
4109 stored_player[i].connected = TRUE;
4111 else if (setup.team_mode && !options.network)
4113 /* try to guess locally connected team mode players (needed for correct
4114 assignment of player figures from level to locally playing players) */
4116 for (i = 0; i < MAX_PLAYERS; i++)
4117 if (setup.input[i].use_joystick ||
4118 setup.input[i].key.left != KSYM_UNDEFINED)
4119 stored_player[i].connected = TRUE;
4123 for (i = 0; i < MAX_PLAYERS; i++)
4124 printf("::: player %d: %s\n", i,
4125 (stored_player[i].connected ? "connected" : "not connected"));
4127 for (i = 0; i < MAX_PLAYERS; i++)
4128 printf("::: player %d: %s\n", i,
4129 (stored_player[i].present ? "present" : "not present"));
4132 /* check if any connected player was not found in playfield */
4133 for (i = 0; i < MAX_PLAYERS; i++)
4135 struct PlayerInfo *player = &stored_player[i];
4137 if (player->connected && !player->present)
4139 struct PlayerInfo *field_player = NULL;
4142 printf("::: looking for field player for player %d ...\n", i);
4145 /* assign first free player found that is present in the playfield */
4147 /* first try: look for unmapped playfield player that is not connected */
4148 if (field_player == NULL)
4149 for (j = 0; j < MAX_PLAYERS; j++)
4150 if (stored_player[j].present &&
4151 !stored_player[j].mapped &&
4152 !stored_player[j].connected)
4153 field_player = &stored_player[j];
4155 /* second try: look for *any* unmapped playfield player */
4156 if (field_player == NULL)
4157 for (j = 0; j < MAX_PLAYERS; j++)
4158 if (stored_player[j].present &&
4159 !stored_player[j].mapped)
4160 field_player = &stored_player[j];
4162 if (field_player != NULL)
4164 int jx = field_player->jx, jy = field_player->jy;
4167 printf("::: found player figure %d\n", field_player->index_nr);
4170 player->present = FALSE;
4171 player->active = FALSE;
4173 field_player->present = TRUE;
4174 field_player->active = TRUE;
4177 player->initial_element = field_player->initial_element;
4178 player->artwork_element = field_player->artwork_element;
4180 player->block_last_field = field_player->block_last_field;
4181 player->block_delay_adjustment = field_player->block_delay_adjustment;
4184 StorePlayer[jx][jy] = field_player->element_nr;
4186 field_player->jx = field_player->last_jx = jx;
4187 field_player->jy = field_player->last_jy = jy;
4189 if (local_player == player)
4190 local_player = field_player;
4192 map_player_action[field_player->index_nr] = i;
4194 field_player->mapped = TRUE;
4197 printf("::: map_player_action[%d] == %d\n",
4198 field_player->index_nr, i);
4203 if (player->connected && player->present)
4204 player->mapped = TRUE;
4209 /* check if any connected player was not found in playfield */
4210 for (i = 0; i < MAX_PLAYERS; i++)
4212 struct PlayerInfo *player = &stored_player[i];
4214 if (player->connected && !player->present)
4216 for (j = 0; j < MAX_PLAYERS; j++)
4218 struct PlayerInfo *field_player = &stored_player[j];
4219 int jx = field_player->jx, jy = field_player->jy;
4221 /* assign first free player found that is present in the playfield */
4222 if (field_player->present && !field_player->connected)
4224 player->present = TRUE;
4225 player->active = TRUE;
4227 field_player->present = FALSE;
4228 field_player->active = FALSE;
4230 player->initial_element = field_player->initial_element;
4231 player->artwork_element = field_player->artwork_element;
4233 player->block_last_field = field_player->block_last_field;
4234 player->block_delay_adjustment = field_player->block_delay_adjustment;
4236 StorePlayer[jx][jy] = player->element_nr;
4238 player->jx = player->last_jx = jx;
4239 player->jy = player->last_jy = jy;
4249 printf("::: local_player->present == %d\n", local_player->present);
4254 /* when playing a tape, eliminate all players who do not participate */
4256 #if USE_NEW_PLAYER_ASSIGNMENTS
4257 for (i = 0; i < MAX_PLAYERS; i++)
4259 if (stored_player[i].active &&
4260 !tape.player_participates[map_player_action[i]])
4262 struct PlayerInfo *player = &stored_player[i];
4263 int jx = player->jx, jy = player->jy;
4265 player->active = FALSE;
4266 StorePlayer[jx][jy] = 0;
4267 Feld[jx][jy] = EL_EMPTY;
4271 for (i = 0; i < MAX_PLAYERS; i++)
4273 if (stored_player[i].active &&
4274 !tape.player_participates[i])
4276 struct PlayerInfo *player = &stored_player[i];
4277 int jx = player->jx, jy = player->jy;
4279 player->active = FALSE;
4280 StorePlayer[jx][jy] = 0;
4281 Feld[jx][jy] = EL_EMPTY;
4286 else if (!options.network && !setup.team_mode) /* && !tape.playing */
4288 /* when in single player mode, eliminate all but the first active player */
4290 for (i = 0; i < MAX_PLAYERS; i++)
4292 if (stored_player[i].active)
4294 for (j = i + 1; j < MAX_PLAYERS; j++)
4296 if (stored_player[j].active)
4298 struct PlayerInfo *player = &stored_player[j];
4299 int jx = player->jx, jy = player->jy;
4301 player->active = FALSE;
4302 player->present = FALSE;
4304 StorePlayer[jx][jy] = 0;
4305 Feld[jx][jy] = EL_EMPTY;
4312 /* when recording the game, store which players take part in the game */
4315 #if USE_NEW_PLAYER_ASSIGNMENTS
4316 for (i = 0; i < MAX_PLAYERS; i++)
4317 if (stored_player[i].connected)
4318 tape.player_participates[i] = TRUE;
4320 for (i = 0; i < MAX_PLAYERS; i++)
4321 if (stored_player[i].active)
4322 tape.player_participates[i] = TRUE;
4328 for (i = 0; i < MAX_PLAYERS; i++)
4330 struct PlayerInfo *player = &stored_player[i];
4332 printf("Player %d: present == %d, connected == %d, active == %d.\n",
4337 if (local_player == player)
4338 printf("Player %d is local player.\n", i+1);
4342 if (BorderElement == EL_EMPTY)
4345 SBX_Right = lev_fieldx - SCR_FIELDX;
4347 SBY_Lower = lev_fieldy - SCR_FIELDY;
4352 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4354 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4357 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4358 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4360 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4361 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4363 /* if local player not found, look for custom element that might create
4364 the player (make some assumptions about the right custom element) */
4365 if (!local_player->present)
4367 int start_x = 0, start_y = 0;
4368 int found_rating = 0;
4369 int found_element = EL_UNDEFINED;
4370 int player_nr = local_player->index_nr;
4372 SCAN_PLAYFIELD(x, y)
4374 int element = Feld[x][y];
4379 if (level.use_start_element[player_nr] &&
4380 level.start_element[player_nr] == element &&
4387 found_element = element;
4390 if (!IS_CUSTOM_ELEMENT(element))
4393 if (CAN_CHANGE(element))
4395 for (i = 0; i < element_info[element].num_change_pages; i++)
4397 /* check for player created from custom element as single target */
4398 content = element_info[element].change_page[i].target_element;
4399 is_player = ELEM_IS_PLAYER(content);
4401 if (is_player && (found_rating < 3 ||
4402 (found_rating == 3 && element < found_element)))
4408 found_element = element;
4413 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4415 /* check for player created from custom element as explosion content */
4416 content = element_info[element].content.e[xx][yy];
4417 is_player = ELEM_IS_PLAYER(content);
4419 if (is_player && (found_rating < 2 ||
4420 (found_rating == 2 && element < found_element)))
4422 start_x = x + xx - 1;
4423 start_y = y + yy - 1;
4426 found_element = element;
4429 if (!CAN_CHANGE(element))
4432 for (i = 0; i < element_info[element].num_change_pages; i++)
4434 /* check for player created from custom element as extended target */
4436 element_info[element].change_page[i].target_content.e[xx][yy];
4438 is_player = ELEM_IS_PLAYER(content);
4440 if (is_player && (found_rating < 1 ||
4441 (found_rating == 1 && element < found_element)))
4443 start_x = x + xx - 1;
4444 start_y = y + yy - 1;
4447 found_element = element;
4453 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
4454 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4457 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4458 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4463 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
4464 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4465 local_player->jx - MIDPOSX);
4467 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4468 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4469 local_player->jy - MIDPOSY);
4473 /* do not use PLAYING mask for fading out from main screen */
4474 game_status = GAME_MODE_MAIN;
4479 if (!game.restart_level)
4480 CloseDoor(DOOR_CLOSE_1);
4483 if (level_editor_test_game)
4484 FadeSkipNextFadeIn();
4486 FadeSetEnterScreen();
4488 if (level_editor_test_game)
4489 fading = fading_none;
4491 fading = menu.destination;
4495 FadeOut(REDRAW_FIELD);
4498 FadeOut(REDRAW_FIELD);
4502 game_status = GAME_MODE_PLAYING;
4505 /* !!! FIX THIS (START) !!! */
4506 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4508 InitGameEngine_EM();
4510 /* blit playfield from scroll buffer to normal back buffer for fading in */
4511 BlitScreenToBitmap_EM(backbuffer);
4513 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4515 InitGameEngine_SP();
4517 /* blit playfield from scroll buffer to normal back buffer for fading in */
4518 BlitScreenToBitmap_SP(backbuffer);
4525 /* after drawing the level, correct some elements */
4526 if (game.timegate_time_left == 0)
4527 CloseAllOpenTimegates();
4529 /* blit playfield from scroll buffer to normal back buffer for fading in */
4530 if (setup.soft_scrolling)
4531 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4533 redraw_mask |= REDRAW_FROM_BACKBUFFER;
4535 /* !!! FIX THIS (END) !!! */
4538 FadeIn(REDRAW_FIELD);
4541 FadeIn(REDRAW_FIELD);
4546 if (!game.restart_level)
4548 /* copy default game door content to main double buffer */
4551 /* !!! CHECK AGAIN !!! */
4552 SetPanelBackground();
4553 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4554 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4556 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
4558 /* (ClearRectangle() only needed if panel bitmap is smaller than panel) */
4559 ClearRectangle(drawto, DX, DY, DXSIZE, DYSIZE);
4560 BlitBitmap(gfx->bitmap, drawto, gfx->src_x, gfx->src_y,
4561 MIN(gfx->width, DXSIZE), MIN(gfx->height, DYSIZE), DX, DY);
4564 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4565 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4569 SetPanelBackground();
4570 SetDrawBackgroundMask(REDRAW_DOOR_1);
4573 UpdateAndDisplayGameControlValues();
4575 UpdateGameDoorValues();
4576 DrawGameDoorValues();
4579 if (!game.restart_level)
4583 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4584 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4585 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4589 /* copy actual game door content to door double buffer for OpenDoor() */
4590 BlitBitmap(drawto, bitmap_db_door,
4591 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4593 OpenDoor(DOOR_OPEN_ALL);
4595 PlaySound(SND_GAME_STARTING);
4597 if (setup.sound_music)
4600 KeyboardAutoRepeatOffUnlessAutoplay();
4604 for (i = 0; i < MAX_PLAYERS; i++)
4605 printf("Player %d %sactive.\n",
4606 i + 1, (stored_player[i].active ? "" : "not "));
4617 game.restart_level = FALSE;
4620 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4622 /* this is used for non-R'n'D game engines to update certain engine values */
4624 /* needed to determine if sounds are played within the visible screen area */
4625 scroll_x = actual_scroll_x;
4626 scroll_y = actual_scroll_y;
4629 void InitMovDir(int x, int y)
4631 int i, element = Feld[x][y];
4632 static int xy[4][2] =
4639 static int direction[3][4] =
4641 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4642 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4643 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4652 Feld[x][y] = EL_BUG;
4653 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4656 case EL_SPACESHIP_RIGHT:
4657 case EL_SPACESHIP_UP:
4658 case EL_SPACESHIP_LEFT:
4659 case EL_SPACESHIP_DOWN:
4660 Feld[x][y] = EL_SPACESHIP;
4661 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4664 case EL_BD_BUTTERFLY_RIGHT:
4665 case EL_BD_BUTTERFLY_UP:
4666 case EL_BD_BUTTERFLY_LEFT:
4667 case EL_BD_BUTTERFLY_DOWN:
4668 Feld[x][y] = EL_BD_BUTTERFLY;
4669 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4672 case EL_BD_FIREFLY_RIGHT:
4673 case EL_BD_FIREFLY_UP:
4674 case EL_BD_FIREFLY_LEFT:
4675 case EL_BD_FIREFLY_DOWN:
4676 Feld[x][y] = EL_BD_FIREFLY;
4677 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4680 case EL_PACMAN_RIGHT:
4682 case EL_PACMAN_LEFT:
4683 case EL_PACMAN_DOWN:
4684 Feld[x][y] = EL_PACMAN;
4685 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4688 case EL_YAMYAM_LEFT:
4689 case EL_YAMYAM_RIGHT:
4691 case EL_YAMYAM_DOWN:
4692 Feld[x][y] = EL_YAMYAM;
4693 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4696 case EL_SP_SNIKSNAK:
4697 MovDir[x][y] = MV_UP;
4700 case EL_SP_ELECTRON:
4701 MovDir[x][y] = MV_LEFT;
4708 Feld[x][y] = EL_MOLE;
4709 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4713 if (IS_CUSTOM_ELEMENT(element))
4715 struct ElementInfo *ei = &element_info[element];
4716 int move_direction_initial = ei->move_direction_initial;
4717 int move_pattern = ei->move_pattern;
4719 if (move_direction_initial == MV_START_PREVIOUS)
4721 if (MovDir[x][y] != MV_NONE)
4724 move_direction_initial = MV_START_AUTOMATIC;
4727 if (move_direction_initial == MV_START_RANDOM)
4728 MovDir[x][y] = 1 << RND(4);
4729 else if (move_direction_initial & MV_ANY_DIRECTION)
4730 MovDir[x][y] = move_direction_initial;
4731 else if (move_pattern == MV_ALL_DIRECTIONS ||
4732 move_pattern == MV_TURNING_LEFT ||
4733 move_pattern == MV_TURNING_RIGHT ||
4734 move_pattern == MV_TURNING_LEFT_RIGHT ||
4735 move_pattern == MV_TURNING_RIGHT_LEFT ||
4736 move_pattern == MV_TURNING_RANDOM)
4737 MovDir[x][y] = 1 << RND(4);
4738 else if (move_pattern == MV_HORIZONTAL)
4739 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4740 else if (move_pattern == MV_VERTICAL)
4741 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4742 else if (move_pattern & MV_ANY_DIRECTION)
4743 MovDir[x][y] = element_info[element].move_pattern;
4744 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4745 move_pattern == MV_ALONG_RIGHT_SIDE)
4747 /* use random direction as default start direction */
4748 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4749 MovDir[x][y] = 1 << RND(4);
4751 for (i = 0; i < NUM_DIRECTIONS; i++)
4753 int x1 = x + xy[i][0];
4754 int y1 = y + xy[i][1];
4756 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4758 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4759 MovDir[x][y] = direction[0][i];
4761 MovDir[x][y] = direction[1][i];
4770 MovDir[x][y] = 1 << RND(4);
4772 if (element != EL_BUG &&
4773 element != EL_SPACESHIP &&
4774 element != EL_BD_BUTTERFLY &&
4775 element != EL_BD_FIREFLY)
4778 for (i = 0; i < NUM_DIRECTIONS; i++)
4780 int x1 = x + xy[i][0];
4781 int y1 = y + xy[i][1];
4783 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4785 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4787 MovDir[x][y] = direction[0][i];
4790 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4791 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4793 MovDir[x][y] = direction[1][i];
4802 GfxDir[x][y] = MovDir[x][y];
4805 void InitAmoebaNr(int x, int y)
4808 int group_nr = AmoebeNachbarNr(x, y);
4812 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4814 if (AmoebaCnt[i] == 0)
4822 AmoebaNr[x][y] = group_nr;
4823 AmoebaCnt[group_nr]++;
4824 AmoebaCnt2[group_nr]++;
4827 static void PlayerWins(struct PlayerInfo *player)
4829 player->LevelSolved = TRUE;
4830 player->GameOver = TRUE;
4832 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4833 level.native_em_level->lev->score : player->score);
4835 player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4836 player->LevelSolved_CountingScore = player->score_final;
4841 static int time, time_final;
4842 static int score, score_final;
4843 static int game_over_delay_1 = 0;
4844 static int game_over_delay_2 = 0;
4845 int game_over_delay_value_1 = 50;
4846 int game_over_delay_value_2 = 50;
4848 if (!local_player->LevelSolved_GameWon)
4852 /* do not start end game actions before the player stops moving (to exit) */
4853 if (local_player->MovPos)
4856 local_player->LevelSolved_GameWon = TRUE;
4857 local_player->LevelSolved_SaveTape = tape.recording;
4858 local_player->LevelSolved_SaveScore = !tape.playing;
4860 if (tape.auto_play) /* tape might already be stopped here */
4861 tape.auto_play_level_solved = TRUE;
4867 game_over_delay_1 = game_over_delay_value_1;
4868 game_over_delay_2 = game_over_delay_value_2;
4870 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4871 score = score_final = local_player->score_final;
4876 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4878 else if (level.time == 0 && TimePlayed < 999)
4881 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4884 local_player->score_final = score_final;
4886 if (level_editor_test_game)
4889 score = score_final;
4892 local_player->LevelSolved_CountingTime = time;
4893 local_player->LevelSolved_CountingScore = score;
4895 game_panel_controls[GAME_PANEL_TIME].value = time;
4896 game_panel_controls[GAME_PANEL_SCORE].value = score;
4898 DisplayGameControlValues();
4900 DrawGameValue_Time(time);
4901 DrawGameValue_Score(score);
4905 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4907 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4909 /* close exit door after last player */
4910 if ((AllPlayersGone &&
4911 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4912 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4913 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4914 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4915 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4917 int element = Feld[ExitX][ExitY];
4920 if (element == EL_EM_EXIT_OPEN ||
4921 element == EL_EM_STEEL_EXIT_OPEN)
4928 Feld[ExitX][ExitY] =
4929 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4930 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4931 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4932 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4933 EL_EM_STEEL_EXIT_CLOSING);
4935 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4939 /* player disappears */
4940 DrawLevelField(ExitX, ExitY);
4943 for (i = 0; i < MAX_PLAYERS; i++)
4945 struct PlayerInfo *player = &stored_player[i];
4947 if (player->present)
4949 RemovePlayer(player);
4951 /* player disappears */
4952 DrawLevelField(player->jx, player->jy);
4957 PlaySound(SND_GAME_WINNING);
4960 if (game_over_delay_1 > 0)
4962 game_over_delay_1--;
4967 if (time != time_final)
4969 int time_to_go = ABS(time_final - time);
4970 int time_count_dir = (time < time_final ? +1 : -1);
4971 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4973 time += time_count_steps * time_count_dir;
4974 score += time_count_steps * level.score[SC_TIME_BONUS];
4977 local_player->LevelSolved_CountingTime = time;
4978 local_player->LevelSolved_CountingScore = score;
4980 game_panel_controls[GAME_PANEL_TIME].value = time;
4981 game_panel_controls[GAME_PANEL_SCORE].value = score;
4983 DisplayGameControlValues();
4985 DrawGameValue_Time(time);
4986 DrawGameValue_Score(score);
4989 if (time == time_final)
4990 StopSound(SND_GAME_LEVELTIME_BONUS);
4991 else if (setup.sound_loops)
4992 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4994 PlaySound(SND_GAME_LEVELTIME_BONUS);
4999 local_player->LevelSolved_PanelOff = TRUE;
5001 if (game_over_delay_2 > 0)
5003 game_over_delay_2--;
5016 boolean raise_level = FALSE;
5018 local_player->LevelSolved_GameEnd = TRUE;
5020 CloseDoor(DOOR_CLOSE_1);
5022 if (local_player->LevelSolved_SaveTape)
5029 SaveTapeChecked(tape.level_nr); /* ask to save tape */
5031 SaveTape(tape.level_nr); /* ask to save tape */
5035 if (level_editor_test_game)
5037 game_status = GAME_MODE_MAIN;
5040 DrawAndFadeInMainMenu(REDRAW_FIELD);
5048 if (!local_player->LevelSolved_SaveScore)
5051 FadeOut(REDRAW_FIELD);
5054 game_status = GAME_MODE_MAIN;
5056 DrawAndFadeInMainMenu(REDRAW_FIELD);
5061 if (level_nr == leveldir_current->handicap_level)
5063 leveldir_current->handicap_level++;
5064 SaveLevelSetup_SeriesInfo();
5067 if (level_nr < leveldir_current->last_level)
5068 raise_level = TRUE; /* advance to next level */
5070 if ((hi_pos = NewHiScore()) >= 0)
5072 game_status = GAME_MODE_SCORES;
5074 DrawHallOfFame(hi_pos);
5085 FadeOut(REDRAW_FIELD);
5088 game_status = GAME_MODE_MAIN;
5096 DrawAndFadeInMainMenu(REDRAW_FIELD);
5105 LoadScore(level_nr);
5107 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5108 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
5111 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
5113 if (local_player->score_final > highscore[k].Score)
5115 /* player has made it to the hall of fame */
5117 if (k < MAX_SCORE_ENTRIES - 1)
5119 int m = MAX_SCORE_ENTRIES - 1;
5122 for (l = k; l < MAX_SCORE_ENTRIES; l++)
5123 if (strEqual(setup.player_name, highscore[l].Name))
5125 if (m == k) /* player's new highscore overwrites his old one */
5129 for (l = m; l > k; l--)
5131 strcpy(highscore[l].Name, highscore[l - 1].Name);
5132 highscore[l].Score = highscore[l - 1].Score;
5139 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5140 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5141 highscore[k].Score = local_player->score_final;
5147 else if (!strncmp(setup.player_name, highscore[k].Name,
5148 MAX_PLAYER_NAME_LEN))
5149 break; /* player already there with a higher score */
5155 SaveScore(level_nr);
5160 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
5162 int element = Feld[x][y];
5163 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5164 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5165 int horiz_move = (dx != 0);
5166 int sign = (horiz_move ? dx : dy);
5167 int step = sign * element_info[element].move_stepsize;
5169 /* special values for move stepsize for spring and things on conveyor belt */
5172 if (CAN_FALL(element) &&
5173 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5174 step = sign * MOVE_STEPSIZE_NORMAL / 2;
5175 else if (element == EL_SPRING)
5176 step = sign * MOVE_STEPSIZE_NORMAL * 2;
5182 inline static int getElementMoveStepsize(int x, int y)
5184 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5187 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5189 if (player->GfxAction != action || player->GfxDir != dir)
5192 printf("Player frame reset! (%d => %d, %d => %d)\n",
5193 player->GfxAction, action, player->GfxDir, dir);
5196 player->GfxAction = action;
5197 player->GfxDir = dir;
5199 player->StepFrame = 0;
5203 #if USE_GFX_RESET_GFX_ANIMATION
5204 static void ResetGfxFrame(int x, int y, boolean redraw)
5206 int element = Feld[x][y];
5207 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5208 int last_gfx_frame = GfxFrame[x][y];
5210 if (graphic_info[graphic].anim_global_sync)
5211 GfxFrame[x][y] = FrameCounter;
5212 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5213 GfxFrame[x][y] = CustomValue[x][y];
5214 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5215 GfxFrame[x][y] = element_info[element].collect_score;
5216 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5217 GfxFrame[x][y] = ChangeDelay[x][y];
5219 if (redraw && GfxFrame[x][y] != last_gfx_frame)
5220 DrawLevelGraphicAnimation(x, y, graphic);
5224 static void ResetGfxAnimation(int x, int y)
5226 GfxAction[x][y] = ACTION_DEFAULT;
5227 GfxDir[x][y] = MovDir[x][y];
5230 #if USE_GFX_RESET_GFX_ANIMATION
5231 ResetGfxFrame(x, y, FALSE);
5235 static void ResetRandomAnimationValue(int x, int y)
5237 GfxRandom[x][y] = INIT_GFX_RANDOM();
5240 void InitMovingField(int x, int y, int direction)
5242 int element = Feld[x][y];
5243 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5244 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5247 boolean is_moving_before, is_moving_after;
5249 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5252 /* check if element was/is moving or being moved before/after mode change */
5255 is_moving_before = (WasJustMoving[x][y] != 0);
5257 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5258 is_moving_before = WasJustMoving[x][y];
5261 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5263 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
5265 /* reset animation only for moving elements which change direction of moving
5266 or which just started or stopped moving
5267 (else CEs with property "can move" / "not moving" are reset each frame) */
5268 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5270 if (is_moving_before != is_moving_after ||
5271 direction != MovDir[x][y])
5272 ResetGfxAnimation(x, y);
5274 if ((is_moving_before || is_moving_after) && !continues_moving)
5275 ResetGfxAnimation(x, y);
5278 if (!continues_moving)
5279 ResetGfxAnimation(x, y);
5282 MovDir[x][y] = direction;
5283 GfxDir[x][y] = direction;
5285 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5286 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5287 direction == MV_DOWN && CAN_FALL(element) ?
5288 ACTION_FALLING : ACTION_MOVING);
5290 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5291 ACTION_FALLING : ACTION_MOVING);
5294 /* this is needed for CEs with property "can move" / "not moving" */
5296 if (is_moving_after)
5298 if (Feld[newx][newy] == EL_EMPTY)
5299 Feld[newx][newy] = EL_BLOCKED;
5301 MovDir[newx][newy] = MovDir[x][y];
5303 #if USE_NEW_CUSTOM_VALUE
5304 CustomValue[newx][newy] = CustomValue[x][y];
5307 GfxFrame[newx][newy] = GfxFrame[x][y];
5308 GfxRandom[newx][newy] = GfxRandom[x][y];
5309 GfxAction[newx][newy] = GfxAction[x][y];
5310 GfxDir[newx][newy] = GfxDir[x][y];
5314 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5316 int direction = MovDir[x][y];
5317 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5318 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
5324 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5326 int oldx = x, oldy = y;
5327 int direction = MovDir[x][y];
5329 if (direction == MV_LEFT)
5331 else if (direction == MV_RIGHT)
5333 else if (direction == MV_UP)
5335 else if (direction == MV_DOWN)
5338 *comes_from_x = oldx;
5339 *comes_from_y = oldy;
5342 int MovingOrBlocked2Element(int x, int y)
5344 int element = Feld[x][y];
5346 if (element == EL_BLOCKED)
5350 Blocked2Moving(x, y, &oldx, &oldy);
5351 return Feld[oldx][oldy];
5357 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5359 /* like MovingOrBlocked2Element(), but if element is moving
5360 and (x,y) is the field the moving element is just leaving,
5361 return EL_BLOCKED instead of the element value */
5362 int element = Feld[x][y];
5364 if (IS_MOVING(x, y))
5366 if (element == EL_BLOCKED)
5370 Blocked2Moving(x, y, &oldx, &oldy);
5371 return Feld[oldx][oldy];
5380 static void RemoveField(int x, int y)
5382 Feld[x][y] = EL_EMPTY;
5388 #if USE_NEW_CUSTOM_VALUE
5389 CustomValue[x][y] = 0;
5393 ChangeDelay[x][y] = 0;
5394 ChangePage[x][y] = -1;
5395 Pushed[x][y] = FALSE;
5398 ExplodeField[x][y] = EX_TYPE_NONE;
5401 GfxElement[x][y] = EL_UNDEFINED;
5402 GfxAction[x][y] = ACTION_DEFAULT;
5403 GfxDir[x][y] = MV_NONE;
5405 /* !!! this would prevent the removed tile from being redrawn !!! */
5406 GfxRedraw[x][y] = GFX_REDRAW_NONE;
5410 void RemoveMovingField(int x, int y)
5412 int oldx = x, oldy = y, newx = x, newy = y;
5413 int element = Feld[x][y];
5414 int next_element = EL_UNDEFINED;
5416 if (element != EL_BLOCKED && !IS_MOVING(x, y))
5419 if (IS_MOVING(x, y))
5421 Moving2Blocked(x, y, &newx, &newy);
5423 if (Feld[newx][newy] != EL_BLOCKED)
5425 /* element is moving, but target field is not free (blocked), but
5426 already occupied by something different (example: acid pool);
5427 in this case, only remove the moving field, but not the target */
5429 RemoveField(oldx, oldy);
5431 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5433 TEST_DrawLevelField(oldx, oldy);
5438 else if (element == EL_BLOCKED)
5440 Blocked2Moving(x, y, &oldx, &oldy);
5441 if (!IS_MOVING(oldx, oldy))
5445 if (element == EL_BLOCKED &&
5446 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5447 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5448 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5449 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5450 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5451 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5452 next_element = get_next_element(Feld[oldx][oldy]);
5454 RemoveField(oldx, oldy);
5455 RemoveField(newx, newy);
5457 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5459 if (next_element != EL_UNDEFINED)
5460 Feld[oldx][oldy] = next_element;
5462 TEST_DrawLevelField(oldx, oldy);
5463 TEST_DrawLevelField(newx, newy);
5466 void DrawDynamite(int x, int y)
5468 int sx = SCREENX(x), sy = SCREENY(y);
5469 int graphic = el2img(Feld[x][y]);
5472 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5475 if (IS_WALKABLE_INSIDE(Back[x][y]))
5479 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5480 else if (Store[x][y])
5481 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5483 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5485 if (Back[x][y] || Store[x][y])
5486 DrawGraphicThruMask(sx, sy, graphic, frame);
5488 DrawGraphic(sx, sy, graphic, frame);
5491 void CheckDynamite(int x, int y)
5493 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5497 if (MovDelay[x][y] != 0)
5500 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5506 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5511 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5513 boolean num_checked_players = 0;
5516 for (i = 0; i < MAX_PLAYERS; i++)
5518 if (stored_player[i].active)
5520 int sx = stored_player[i].jx;
5521 int sy = stored_player[i].jy;
5523 if (num_checked_players == 0)
5530 *sx1 = MIN(*sx1, sx);
5531 *sy1 = MIN(*sy1, sy);
5532 *sx2 = MAX(*sx2, sx);
5533 *sy2 = MAX(*sy2, sy);
5536 num_checked_players++;
5541 static boolean checkIfAllPlayersFitToScreen_RND()
5543 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5545 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5547 return (sx2 - sx1 < SCR_FIELDX &&
5548 sy2 - sy1 < SCR_FIELDY);
5551 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5553 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5555 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5557 *sx = (sx1 + sx2) / 2;
5558 *sy = (sy1 + sy2) / 2;
5561 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5562 boolean center_screen, boolean quick_relocation)
5564 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5565 boolean no_delay = (tape.warp_forward);
5566 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5567 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5569 if (quick_relocation)
5571 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5573 if (!level.shifted_relocation || center_screen)
5575 /* quick relocation (without scrolling), with centering of screen */
5577 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5578 x > SBX_Right + MIDPOSX ? SBX_Right :
5581 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5582 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5587 /* quick relocation (without scrolling), but do not center screen */
5589 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5590 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5593 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5594 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5597 int offset_x = x + (scroll_x - center_scroll_x);
5598 int offset_y = y + (scroll_y - center_scroll_y);
5600 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5601 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5602 offset_x - MIDPOSX);
5604 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5605 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5606 offset_y - MIDPOSY);
5612 if (!level.shifted_relocation || center_screen)
5614 /* quick relocation (without scrolling), with centering of screen */
5616 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5617 x > SBX_Right + MIDPOSX ? SBX_Right :
5620 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5621 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5626 /* quick relocation (without scrolling), but do not center screen */
5628 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5629 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5632 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5633 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5636 int offset_x = x + (scroll_x - center_scroll_x);
5637 int offset_y = y + (scroll_y - center_scroll_y);
5639 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5640 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5641 offset_x - MIDPOSX);
5643 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5644 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5645 offset_y - MIDPOSY);
5648 /* quick relocation (without scrolling), inside visible screen area */
5650 int offset = game.scroll_delay_value;
5652 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
5653 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5654 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5656 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
5657 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5658 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5660 /* don't scroll over playfield boundaries */
5661 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5662 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5664 /* don't scroll over playfield boundaries */
5665 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5666 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5670 RedrawPlayfield(TRUE, 0,0,0,0);
5675 int scroll_xx, scroll_yy;
5677 if (!level.shifted_relocation || center_screen)
5679 /* visible relocation (with scrolling), with centering of screen */
5681 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5682 x > SBX_Right + MIDPOSX ? SBX_Right :
5685 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5686 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5691 /* visible relocation (with scrolling), but do not center screen */
5693 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5694 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5697 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5698 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5701 int offset_x = x + (scroll_x - center_scroll_x);
5702 int offset_y = y + (scroll_y - center_scroll_y);
5704 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5705 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5706 offset_x - MIDPOSX);
5708 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5709 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5710 offset_y - MIDPOSY);
5715 /* visible relocation (with scrolling), with centering of screen */
5717 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5718 x > SBX_Right + MIDPOSX ? SBX_Right :
5721 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5722 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5726 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5728 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5731 int fx = FX, fy = FY;
5733 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5734 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5736 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5742 fx += dx * TILEX / 2;
5743 fy += dy * TILEY / 2;
5745 ScrollLevel(dx, dy);
5748 /* scroll in two steps of half tile size to make things smoother */
5749 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5751 Delay(wait_delay_value);
5753 /* scroll second step to align at full tile size */
5755 Delay(wait_delay_value);
5760 Delay(wait_delay_value);
5764 void RelocatePlayer(int jx, int jy, int el_player_raw)
5766 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5767 int player_nr = GET_PLAYER_NR(el_player);
5768 struct PlayerInfo *player = &stored_player[player_nr];
5769 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5770 boolean no_delay = (tape.warp_forward);
5771 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5772 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5773 int old_jx = player->jx;
5774 int old_jy = player->jy;
5775 int old_element = Feld[old_jx][old_jy];
5776 int element = Feld[jx][jy];
5777 boolean player_relocated = (old_jx != jx || old_jy != jy);
5779 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5780 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5781 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5782 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5783 int leave_side_horiz = move_dir_horiz;
5784 int leave_side_vert = move_dir_vert;
5785 int enter_side = enter_side_horiz | enter_side_vert;
5786 int leave_side = leave_side_horiz | leave_side_vert;
5788 if (player->GameOver) /* do not reanimate dead player */
5791 if (!player_relocated) /* no need to relocate the player */
5794 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5796 RemoveField(jx, jy); /* temporarily remove newly placed player */
5797 DrawLevelField(jx, jy);
5800 if (player->present)
5802 while (player->MovPos)
5804 ScrollPlayer(player, SCROLL_GO_ON);
5805 ScrollScreen(NULL, SCROLL_GO_ON);
5807 AdvanceFrameAndPlayerCounters(player->index_nr);
5812 Delay(wait_delay_value);
5815 DrawPlayer(player); /* needed here only to cleanup last field */
5816 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5818 player->is_moving = FALSE;
5821 if (IS_CUSTOM_ELEMENT(old_element))
5822 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5824 player->index_bit, leave_side);
5826 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5828 player->index_bit, leave_side);
5830 Feld[jx][jy] = el_player;
5831 InitPlayerField(jx, jy, el_player, TRUE);
5833 /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5834 possible that the relocation target field did not contain a player element,
5835 but a walkable element, to which the new player was relocated -- in this
5836 case, restore that (already initialized!) element on the player field */
5837 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5839 Feld[jx][jy] = element; /* restore previously existing element */
5841 /* !!! do not initialize already initialized element a second time !!! */
5842 /* (this causes at least problems with "element creation" CE trigger for
5843 already existing elements, and existing Sokoban fields counted twice) */
5844 InitField(jx, jy, FALSE);
5848 /* only visually relocate centered player */
5849 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5850 FALSE, level.instant_relocation);
5852 TestIfPlayerTouchesBadThing(jx, jy);
5853 TestIfPlayerTouchesCustomElement(jx, jy);
5855 if (IS_CUSTOM_ELEMENT(element))
5856 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5857 player->index_bit, enter_side);
5859 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5860 player->index_bit, enter_side);
5863 if (player->is_switching)
5865 /* ensure that relocation while still switching an element does not cause
5866 a new element to be treated as also switched directly after relocation
5867 (this is important for teleporter switches that teleport the player to
5868 a place where another teleporter switch is in the same direction, which
5869 would then incorrectly be treated as immediately switched before the
5870 direction key that caused the switch was released) */
5872 player->switch_x += jx - old_jx;
5873 player->switch_y += jy - old_jy;
5878 void Explode(int ex, int ey, int phase, int mode)
5884 /* !!! eliminate this variable !!! */
5885 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5887 if (game.explosions_delayed)
5889 ExplodeField[ex][ey] = mode;
5893 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5895 int center_element = Feld[ex][ey];
5896 int artwork_element, explosion_element; /* set these values later */
5899 /* --- This is only really needed (and now handled) in "Impact()". --- */
5900 /* do not explode moving elements that left the explode field in time */
5901 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5902 center_element == EL_EMPTY &&
5903 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5908 /* !!! at this place, the center element may be EL_BLOCKED !!! */
5909 if (mode == EX_TYPE_NORMAL ||
5910 mode == EX_TYPE_CENTER ||
5911 mode == EX_TYPE_CROSS)
5912 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5915 /* remove things displayed in background while burning dynamite */
5916 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5919 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5921 /* put moving element to center field (and let it explode there) */
5922 center_element = MovingOrBlocked2Element(ex, ey);
5923 RemoveMovingField(ex, ey);
5924 Feld[ex][ey] = center_element;
5927 /* now "center_element" is finally determined -- set related values now */
5928 artwork_element = center_element; /* for custom player artwork */
5929 explosion_element = center_element; /* for custom player artwork */
5931 if (IS_PLAYER(ex, ey))
5933 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5935 artwork_element = stored_player[player_nr].artwork_element;
5937 if (level.use_explosion_element[player_nr])
5939 explosion_element = level.explosion_element[player_nr];
5940 artwork_element = explosion_element;
5945 if (mode == EX_TYPE_NORMAL ||
5946 mode == EX_TYPE_CENTER ||
5947 mode == EX_TYPE_CROSS)
5948 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5951 last_phase = element_info[explosion_element].explosion_delay + 1;
5953 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5955 int xx = x - ex + 1;
5956 int yy = y - ey + 1;
5959 if (!IN_LEV_FIELD(x, y) ||
5960 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5961 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5964 element = Feld[x][y];
5966 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5968 element = MovingOrBlocked2Element(x, y);
5970 if (!IS_EXPLOSION_PROOF(element))
5971 RemoveMovingField(x, y);
5974 /* indestructible elements can only explode in center (but not flames) */
5975 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5976 mode == EX_TYPE_BORDER)) ||
5977 element == EL_FLAMES)
5980 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5981 behaviour, for example when touching a yamyam that explodes to rocks
5982 with active deadly shield, a rock is created under the player !!! */
5983 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5985 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5986 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5987 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5989 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5992 if (IS_ACTIVE_BOMB(element))
5994 /* re-activate things under the bomb like gate or penguin */
5995 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
6002 /* save walkable background elements while explosion on same tile */
6003 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
6004 (x != ex || y != ey || mode == EX_TYPE_BORDER))
6005 Back[x][y] = element;
6007 /* ignite explodable elements reached by other explosion */
6008 if (element == EL_EXPLOSION)
6009 element = Store2[x][y];
6011 if (AmoebaNr[x][y] &&
6012 (element == EL_AMOEBA_FULL ||
6013 element == EL_BD_AMOEBA ||
6014 element == EL_AMOEBA_GROWING))
6016 AmoebaCnt[AmoebaNr[x][y]]--;
6017 AmoebaCnt2[AmoebaNr[x][y]]--;
6022 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
6024 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
6026 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
6028 if (PLAYERINFO(ex, ey)->use_murphy)
6029 Store[x][y] = EL_EMPTY;
6032 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
6033 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
6034 else if (ELEM_IS_PLAYER(center_element))
6035 Store[x][y] = EL_EMPTY;
6036 else if (center_element == EL_YAMYAM)
6037 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
6038 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
6039 Store[x][y] = element_info[center_element].content.e[xx][yy];
6041 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6042 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6043 otherwise) -- FIX THIS !!! */
6044 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6045 Store[x][y] = element_info[element].content.e[1][1];
6047 else if (!CAN_EXPLODE(element))
6048 Store[x][y] = element_info[element].content.e[1][1];
6051 Store[x][y] = EL_EMPTY;
6053 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6054 center_element == EL_AMOEBA_TO_DIAMOND)
6055 Store2[x][y] = element;
6057 Feld[x][y] = EL_EXPLOSION;
6058 GfxElement[x][y] = artwork_element;
6060 ExplodePhase[x][y] = 1;
6061 ExplodeDelay[x][y] = last_phase;
6066 if (center_element == EL_YAMYAM)
6067 game.yamyam_content_nr =
6068 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6080 GfxFrame[x][y] = 0; /* restart explosion animation */
6082 last_phase = ExplodeDelay[x][y];
6084 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6088 /* activate this even in non-DEBUG version until cause for crash in
6089 getGraphicAnimationFrame() (see below) is found and eliminated */
6095 /* this can happen if the player leaves an explosion just in time */
6096 if (GfxElement[x][y] == EL_UNDEFINED)
6097 GfxElement[x][y] = EL_EMPTY;
6099 if (GfxElement[x][y] == EL_UNDEFINED)
6102 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
6103 printf("Explode(): This should never happen!\n");
6106 GfxElement[x][y] = EL_EMPTY;
6112 border_element = Store2[x][y];
6113 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6114 border_element = StorePlayer[x][y];
6116 if (phase == element_info[border_element].ignition_delay ||
6117 phase == last_phase)
6119 boolean border_explosion = FALSE;
6121 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6122 !PLAYER_EXPLOSION_PROTECTED(x, y))
6124 KillPlayerUnlessExplosionProtected(x, y);
6125 border_explosion = TRUE;
6127 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6129 Feld[x][y] = Store2[x][y];
6132 border_explosion = TRUE;
6134 else if (border_element == EL_AMOEBA_TO_DIAMOND)
6136 AmoebeUmwandeln(x, y);
6138 border_explosion = TRUE;
6141 /* if an element just explodes due to another explosion (chain-reaction),
6142 do not immediately end the new explosion when it was the last frame of
6143 the explosion (as it would be done in the following "if"-statement!) */
6144 if (border_explosion && phase == last_phase)
6148 if (phase == last_phase)
6152 element = Feld[x][y] = Store[x][y];
6153 Store[x][y] = Store2[x][y] = 0;
6154 GfxElement[x][y] = EL_UNDEFINED;
6156 /* player can escape from explosions and might therefore be still alive */
6157 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6158 element <= EL_PLAYER_IS_EXPLODING_4)
6160 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6161 int explosion_element = EL_PLAYER_1 + player_nr;
6162 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6163 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6165 if (level.use_explosion_element[player_nr])
6166 explosion_element = level.explosion_element[player_nr];
6168 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6169 element_info[explosion_element].content.e[xx][yy]);
6172 /* restore probably existing indestructible background element */
6173 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6174 element = Feld[x][y] = Back[x][y];
6177 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6178 GfxDir[x][y] = MV_NONE;
6179 ChangeDelay[x][y] = 0;
6180 ChangePage[x][y] = -1;
6182 #if USE_NEW_CUSTOM_VALUE
6183 CustomValue[x][y] = 0;
6186 InitField_WithBug2(x, y, FALSE);
6188 TEST_DrawLevelField(x, y);
6190 TestIfElementTouchesCustomElement(x, y);
6192 if (GFX_CRUMBLED(element))
6193 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6195 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6196 StorePlayer[x][y] = 0;
6198 if (ELEM_IS_PLAYER(element))
6199 RelocatePlayer(x, y, element);
6201 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6203 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6204 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6207 TEST_DrawLevelFieldCrumbled(x, y);
6209 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6211 DrawLevelElement(x, y, Back[x][y]);
6212 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6214 else if (IS_WALKABLE_UNDER(Back[x][y]))
6216 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6217 DrawLevelElementThruMask(x, y, Back[x][y]);
6219 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6220 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6224 void DynaExplode(int ex, int ey)
6227 int dynabomb_element = Feld[ex][ey];
6228 int dynabomb_size = 1;
6229 boolean dynabomb_xl = FALSE;
6230 struct PlayerInfo *player;
6231 static int xy[4][2] =
6239 if (IS_ACTIVE_BOMB(dynabomb_element))
6241 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6242 dynabomb_size = player->dynabomb_size;
6243 dynabomb_xl = player->dynabomb_xl;
6244 player->dynabombs_left++;
6247 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6249 for (i = 0; i < NUM_DIRECTIONS; i++)
6251 for (j = 1; j <= dynabomb_size; j++)
6253 int x = ex + j * xy[i][0];
6254 int y = ey + j * xy[i][1];
6257 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6260 element = Feld[x][y];
6262 /* do not restart explosions of fields with active bombs */
6263 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6266 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6268 if (element != EL_EMPTY && element != EL_EXPLOSION &&
6269 !IS_DIGGABLE(element) && !dynabomb_xl)
6275 void Bang(int x, int y)
6277 int element = MovingOrBlocked2Element(x, y);
6278 int explosion_type = EX_TYPE_NORMAL;
6280 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6282 struct PlayerInfo *player = PLAYERINFO(x, y);
6284 #if USE_FIX_CE_ACTION_WITH_PLAYER
6285 element = Feld[x][y] = player->initial_element;
6287 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6288 player->element_nr);
6291 if (level.use_explosion_element[player->index_nr])
6293 int explosion_element = level.explosion_element[player->index_nr];
6295 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6296 explosion_type = EX_TYPE_CROSS;
6297 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6298 explosion_type = EX_TYPE_CENTER;
6306 case EL_BD_BUTTERFLY:
6309 case EL_DARK_YAMYAM:
6313 RaiseScoreElement(element);
6316 case EL_DYNABOMB_PLAYER_1_ACTIVE:
6317 case EL_DYNABOMB_PLAYER_2_ACTIVE:
6318 case EL_DYNABOMB_PLAYER_3_ACTIVE:
6319 case EL_DYNABOMB_PLAYER_4_ACTIVE:
6320 case EL_DYNABOMB_INCREASE_NUMBER:
6321 case EL_DYNABOMB_INCREASE_SIZE:
6322 case EL_DYNABOMB_INCREASE_POWER:
6323 explosion_type = EX_TYPE_DYNA;
6326 case EL_DC_LANDMINE:
6328 case EL_EM_EXIT_OPEN:
6329 case EL_EM_STEEL_EXIT_OPEN:
6331 explosion_type = EX_TYPE_CENTER;
6336 case EL_LAMP_ACTIVE:
6337 case EL_AMOEBA_TO_DIAMOND:
6338 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
6339 explosion_type = EX_TYPE_CENTER;
6343 if (element_info[element].explosion_type == EXPLODES_CROSS)
6344 explosion_type = EX_TYPE_CROSS;
6345 else if (element_info[element].explosion_type == EXPLODES_1X1)
6346 explosion_type = EX_TYPE_CENTER;
6350 if (explosion_type == EX_TYPE_DYNA)
6353 Explode(x, y, EX_PHASE_START, explosion_type);
6355 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6358 void SplashAcid(int x, int y)
6360 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6361 (!IN_LEV_FIELD(x - 1, y - 2) ||
6362 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6363 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6365 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6366 (!IN_LEV_FIELD(x + 1, y - 2) ||
6367 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6368 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6370 PlayLevelSound(x, y, SND_ACID_SPLASHING);
6373 static void InitBeltMovement()
6375 static int belt_base_element[4] =
6377 EL_CONVEYOR_BELT_1_LEFT,
6378 EL_CONVEYOR_BELT_2_LEFT,
6379 EL_CONVEYOR_BELT_3_LEFT,
6380 EL_CONVEYOR_BELT_4_LEFT
6382 static int belt_base_active_element[4] =
6384 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6385 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6386 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6387 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6392 /* set frame order for belt animation graphic according to belt direction */
6393 for (i = 0; i < NUM_BELTS; i++)
6397 for (j = 0; j < NUM_BELT_PARTS; j++)
6399 int element = belt_base_active_element[belt_nr] + j;
6400 int graphic_1 = el2img(element);
6401 int graphic_2 = el2panelimg(element);
6403 if (game.belt_dir[i] == MV_LEFT)
6405 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6406 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6410 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6411 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6416 SCAN_PLAYFIELD(x, y)
6418 int element = Feld[x][y];
6420 for (i = 0; i < NUM_BELTS; i++)
6422 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6424 int e_belt_nr = getBeltNrFromBeltElement(element);
6427 if (e_belt_nr == belt_nr)
6429 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6431 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6438 static void ToggleBeltSwitch(int x, int y)
6440 static int belt_base_element[4] =
6442 EL_CONVEYOR_BELT_1_LEFT,
6443 EL_CONVEYOR_BELT_2_LEFT,
6444 EL_CONVEYOR_BELT_3_LEFT,
6445 EL_CONVEYOR_BELT_4_LEFT
6447 static int belt_base_active_element[4] =
6449 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6450 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6451 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6452 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6454 static int belt_base_switch_element[4] =
6456 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6457 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6458 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6459 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6461 static int belt_move_dir[4] =
6469 int element = Feld[x][y];
6470 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6471 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6472 int belt_dir = belt_move_dir[belt_dir_nr];
6475 if (!IS_BELT_SWITCH(element))
6478 game.belt_dir_nr[belt_nr] = belt_dir_nr;
6479 game.belt_dir[belt_nr] = belt_dir;
6481 if (belt_dir_nr == 3)
6484 /* set frame order for belt animation graphic according to belt direction */
6485 for (i = 0; i < NUM_BELT_PARTS; i++)
6487 int element = belt_base_active_element[belt_nr] + i;
6488 int graphic_1 = el2img(element);
6489 int graphic_2 = el2panelimg(element);
6491 if (belt_dir == MV_LEFT)
6493 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6494 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6498 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6499 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6503 SCAN_PLAYFIELD(xx, yy)
6505 int element = Feld[xx][yy];
6507 if (IS_BELT_SWITCH(element))
6509 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6511 if (e_belt_nr == belt_nr)
6513 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6514 TEST_DrawLevelField(xx, yy);
6517 else if (IS_BELT(element) && belt_dir != MV_NONE)
6519 int e_belt_nr = getBeltNrFromBeltElement(element);
6521 if (e_belt_nr == belt_nr)
6523 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6525 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6526 TEST_DrawLevelField(xx, yy);
6529 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6531 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6533 if (e_belt_nr == belt_nr)
6535 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6537 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6538 TEST_DrawLevelField(xx, yy);
6544 static void ToggleSwitchgateSwitch(int x, int y)
6548 game.switchgate_pos = !game.switchgate_pos;
6550 SCAN_PLAYFIELD(xx, yy)
6552 int element = Feld[xx][yy];
6554 #if !USE_BOTH_SWITCHGATE_SWITCHES
6555 if (element == EL_SWITCHGATE_SWITCH_UP ||
6556 element == EL_SWITCHGATE_SWITCH_DOWN)
6558 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6559 TEST_DrawLevelField(xx, yy);
6561 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6562 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6564 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6565 TEST_DrawLevelField(xx, yy);
6568 if (element == EL_SWITCHGATE_SWITCH_UP)
6570 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6571 TEST_DrawLevelField(xx, yy);
6573 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6575 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6576 TEST_DrawLevelField(xx, yy);
6578 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6580 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6581 TEST_DrawLevelField(xx, yy);
6583 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6585 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6586 TEST_DrawLevelField(xx, yy);
6589 else if (element == EL_SWITCHGATE_OPEN ||
6590 element == EL_SWITCHGATE_OPENING)
6592 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6594 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6596 else if (element == EL_SWITCHGATE_CLOSED ||
6597 element == EL_SWITCHGATE_CLOSING)
6599 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6601 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6606 static int getInvisibleActiveFromInvisibleElement(int element)
6608 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6609 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6610 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6614 static int getInvisibleFromInvisibleActiveElement(int element)
6616 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6617 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6618 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6622 static void RedrawAllLightSwitchesAndInvisibleElements()
6626 SCAN_PLAYFIELD(x, y)
6628 int element = Feld[x][y];
6630 if (element == EL_LIGHT_SWITCH &&
6631 game.light_time_left > 0)
6633 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6634 TEST_DrawLevelField(x, y);
6636 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6637 game.light_time_left == 0)
6639 Feld[x][y] = EL_LIGHT_SWITCH;
6640 TEST_DrawLevelField(x, y);
6642 else if (element == EL_EMC_DRIPPER &&
6643 game.light_time_left > 0)
6645 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6646 TEST_DrawLevelField(x, y);
6648 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6649 game.light_time_left == 0)
6651 Feld[x][y] = EL_EMC_DRIPPER;
6652 TEST_DrawLevelField(x, y);
6654 else if (element == EL_INVISIBLE_STEELWALL ||
6655 element == EL_INVISIBLE_WALL ||
6656 element == EL_INVISIBLE_SAND)
6658 if (game.light_time_left > 0)
6659 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6661 TEST_DrawLevelField(x, y);
6663 /* uncrumble neighbour fields, if needed */
6664 if (element == EL_INVISIBLE_SAND)
6665 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6667 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6668 element == EL_INVISIBLE_WALL_ACTIVE ||
6669 element == EL_INVISIBLE_SAND_ACTIVE)
6671 if (game.light_time_left == 0)
6672 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6674 TEST_DrawLevelField(x, y);
6676 /* re-crumble neighbour fields, if needed */
6677 if (element == EL_INVISIBLE_SAND)
6678 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6683 static void RedrawAllInvisibleElementsForLenses()
6687 SCAN_PLAYFIELD(x, y)
6689 int element = Feld[x][y];
6691 if (element == EL_EMC_DRIPPER &&
6692 game.lenses_time_left > 0)
6694 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6695 TEST_DrawLevelField(x, y);
6697 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6698 game.lenses_time_left == 0)
6700 Feld[x][y] = EL_EMC_DRIPPER;
6701 TEST_DrawLevelField(x, y);
6703 else if (element == EL_INVISIBLE_STEELWALL ||
6704 element == EL_INVISIBLE_WALL ||
6705 element == EL_INVISIBLE_SAND)
6707 if (game.lenses_time_left > 0)
6708 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6710 TEST_DrawLevelField(x, y);
6712 /* uncrumble neighbour fields, if needed */
6713 if (element == EL_INVISIBLE_SAND)
6714 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6716 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6717 element == EL_INVISIBLE_WALL_ACTIVE ||
6718 element == EL_INVISIBLE_SAND_ACTIVE)
6720 if (game.lenses_time_left == 0)
6721 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6723 TEST_DrawLevelField(x, y);
6725 /* re-crumble neighbour fields, if needed */
6726 if (element == EL_INVISIBLE_SAND)
6727 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6732 static void RedrawAllInvisibleElementsForMagnifier()
6736 SCAN_PLAYFIELD(x, y)
6738 int element = Feld[x][y];
6740 if (element == EL_EMC_FAKE_GRASS &&
6741 game.magnify_time_left > 0)
6743 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6744 TEST_DrawLevelField(x, y);
6746 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6747 game.magnify_time_left == 0)
6749 Feld[x][y] = EL_EMC_FAKE_GRASS;
6750 TEST_DrawLevelField(x, y);
6752 else if (IS_GATE_GRAY(element) &&
6753 game.magnify_time_left > 0)
6755 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6756 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6757 IS_EM_GATE_GRAY(element) ?
6758 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6759 IS_EMC_GATE_GRAY(element) ?
6760 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6761 IS_DC_GATE_GRAY(element) ?
6762 EL_DC_GATE_WHITE_GRAY_ACTIVE :
6764 TEST_DrawLevelField(x, y);
6766 else if (IS_GATE_GRAY_ACTIVE(element) &&
6767 game.magnify_time_left == 0)
6769 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6770 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6771 IS_EM_GATE_GRAY_ACTIVE(element) ?
6772 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6773 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6774 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6775 IS_DC_GATE_GRAY_ACTIVE(element) ?
6776 EL_DC_GATE_WHITE_GRAY :
6778 TEST_DrawLevelField(x, y);
6783 static void ToggleLightSwitch(int x, int y)
6785 int element = Feld[x][y];
6787 game.light_time_left =
6788 (element == EL_LIGHT_SWITCH ?
6789 level.time_light * FRAMES_PER_SECOND : 0);
6791 RedrawAllLightSwitchesAndInvisibleElements();
6794 static void ActivateTimegateSwitch(int x, int y)
6798 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6800 SCAN_PLAYFIELD(xx, yy)
6802 int element = Feld[xx][yy];
6804 if (element == EL_TIMEGATE_CLOSED ||
6805 element == EL_TIMEGATE_CLOSING)
6807 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6808 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6812 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6814 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6815 TEST_DrawLevelField(xx, yy);
6822 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6823 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6825 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6829 void Impact(int x, int y)
6831 boolean last_line = (y == lev_fieldy - 1);
6832 boolean object_hit = FALSE;
6833 boolean impact = (last_line || object_hit);
6834 int element = Feld[x][y];
6835 int smashed = EL_STEELWALL;
6837 if (!last_line) /* check if element below was hit */
6839 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6842 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6843 MovDir[x][y + 1] != MV_DOWN ||
6844 MovPos[x][y + 1] <= TILEY / 2));
6846 /* do not smash moving elements that left the smashed field in time */
6847 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6848 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6851 #if USE_QUICKSAND_IMPACT_BUGFIX
6852 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6854 RemoveMovingField(x, y + 1);
6855 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6856 Feld[x][y + 2] = EL_ROCK;
6857 TEST_DrawLevelField(x, y + 2);
6862 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6864 RemoveMovingField(x, y + 1);
6865 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6866 Feld[x][y + 2] = EL_ROCK;
6867 TEST_DrawLevelField(x, y + 2);
6874 smashed = MovingOrBlocked2Element(x, y + 1);
6876 impact = (last_line || object_hit);
6879 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6881 SplashAcid(x, y + 1);
6885 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6886 /* only reset graphic animation if graphic really changes after impact */
6888 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6890 ResetGfxAnimation(x, y);
6891 TEST_DrawLevelField(x, y);
6894 if (impact && CAN_EXPLODE_IMPACT(element))
6899 else if (impact && element == EL_PEARL &&
6900 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6902 ResetGfxAnimation(x, y);
6904 Feld[x][y] = EL_PEARL_BREAKING;
6905 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6908 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6910 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6915 if (impact && element == EL_AMOEBA_DROP)
6917 if (object_hit && IS_PLAYER(x, y + 1))
6918 KillPlayerUnlessEnemyProtected(x, y + 1);
6919 else if (object_hit && smashed == EL_PENGUIN)
6923 Feld[x][y] = EL_AMOEBA_GROWING;
6924 Store[x][y] = EL_AMOEBA_WET;
6926 ResetRandomAnimationValue(x, y);
6931 if (object_hit) /* check which object was hit */
6933 if ((CAN_PASS_MAGIC_WALL(element) &&
6934 (smashed == EL_MAGIC_WALL ||
6935 smashed == EL_BD_MAGIC_WALL)) ||
6936 (CAN_PASS_DC_MAGIC_WALL(element) &&
6937 smashed == EL_DC_MAGIC_WALL))
6940 int activated_magic_wall =
6941 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6942 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6943 EL_DC_MAGIC_WALL_ACTIVE);
6945 /* activate magic wall / mill */
6946 SCAN_PLAYFIELD(xx, yy)
6948 if (Feld[xx][yy] == smashed)
6949 Feld[xx][yy] = activated_magic_wall;
6952 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6953 game.magic_wall_active = TRUE;
6955 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6956 SND_MAGIC_WALL_ACTIVATING :
6957 smashed == EL_BD_MAGIC_WALL ?
6958 SND_BD_MAGIC_WALL_ACTIVATING :
6959 SND_DC_MAGIC_WALL_ACTIVATING));
6962 if (IS_PLAYER(x, y + 1))
6964 if (CAN_SMASH_PLAYER(element))
6966 KillPlayerUnlessEnemyProtected(x, y + 1);
6970 else if (smashed == EL_PENGUIN)
6972 if (CAN_SMASH_PLAYER(element))
6978 else if (element == EL_BD_DIAMOND)
6980 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6986 else if (((element == EL_SP_INFOTRON ||
6987 element == EL_SP_ZONK) &&
6988 (smashed == EL_SP_SNIKSNAK ||
6989 smashed == EL_SP_ELECTRON ||
6990 smashed == EL_SP_DISK_ORANGE)) ||
6991 (element == EL_SP_INFOTRON &&
6992 smashed == EL_SP_DISK_YELLOW))
6997 else if (CAN_SMASH_EVERYTHING(element))
6999 if (IS_CLASSIC_ENEMY(smashed) ||
7000 CAN_EXPLODE_SMASHED(smashed))
7005 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
7007 if (smashed == EL_LAMP ||
7008 smashed == EL_LAMP_ACTIVE)
7013 else if (smashed == EL_NUT)
7015 Feld[x][y + 1] = EL_NUT_BREAKING;
7016 PlayLevelSound(x, y, SND_NUT_BREAKING);
7017 RaiseScoreElement(EL_NUT);
7020 else if (smashed == EL_PEARL)
7022 ResetGfxAnimation(x, y);
7024 Feld[x][y + 1] = EL_PEARL_BREAKING;
7025 PlayLevelSound(x, y, SND_PEARL_BREAKING);
7028 else if (smashed == EL_DIAMOND)
7030 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
7031 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
7034 else if (IS_BELT_SWITCH(smashed))
7036 ToggleBeltSwitch(x, y + 1);
7038 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
7039 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
7040 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
7041 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
7043 ToggleSwitchgateSwitch(x, y + 1);
7045 else if (smashed == EL_LIGHT_SWITCH ||
7046 smashed == EL_LIGHT_SWITCH_ACTIVE)
7048 ToggleLightSwitch(x, y + 1);
7053 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
7056 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7058 CheckElementChangeBySide(x, y + 1, smashed, element,
7059 CE_SWITCHED, CH_SIDE_TOP);
7060 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7066 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7071 /* play sound of magic wall / mill */
7073 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7074 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7075 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7077 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7078 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7079 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7080 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7081 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7082 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7087 /* play sound of object that hits the ground */
7088 if (last_line || object_hit)
7089 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7092 inline static void TurnRoundExt(int x, int y)
7104 { 0, 0 }, { 0, 0 }, { 0, 0 },
7109 int left, right, back;
7113 { MV_DOWN, MV_UP, MV_RIGHT },
7114 { MV_UP, MV_DOWN, MV_LEFT },
7116 { MV_LEFT, MV_RIGHT, MV_DOWN },
7120 { MV_RIGHT, MV_LEFT, MV_UP }
7123 int element = Feld[x][y];
7124 int move_pattern = element_info[element].move_pattern;
7126 int old_move_dir = MovDir[x][y];
7127 int left_dir = turn[old_move_dir].left;
7128 int right_dir = turn[old_move_dir].right;
7129 int back_dir = turn[old_move_dir].back;
7131 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
7132 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
7133 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
7134 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
7136 int left_x = x + left_dx, left_y = y + left_dy;
7137 int right_x = x + right_dx, right_y = y + right_dy;
7138 int move_x = x + move_dx, move_y = y + move_dy;
7142 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7144 TestIfBadThingTouchesOtherBadThing(x, y);
7146 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7147 MovDir[x][y] = right_dir;
7148 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7149 MovDir[x][y] = left_dir;
7151 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7153 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
7156 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7158 TestIfBadThingTouchesOtherBadThing(x, y);
7160 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7161 MovDir[x][y] = left_dir;
7162 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7163 MovDir[x][y] = right_dir;
7165 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7167 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
7170 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7172 TestIfBadThingTouchesOtherBadThing(x, y);
7174 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7175 MovDir[x][y] = left_dir;
7176 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7177 MovDir[x][y] = right_dir;
7179 if (MovDir[x][y] != old_move_dir)
7182 else if (element == EL_YAMYAM)
7184 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7185 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7187 if (can_turn_left && can_turn_right)
7188 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7189 else if (can_turn_left)
7190 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7191 else if (can_turn_right)
7192 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7194 MovDir[x][y] = back_dir;
7196 MovDelay[x][y] = 16 + 16 * RND(3);
7198 else if (element == EL_DARK_YAMYAM)
7200 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7202 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7205 if (can_turn_left && can_turn_right)
7206 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7207 else if (can_turn_left)
7208 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7209 else if (can_turn_right)
7210 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7212 MovDir[x][y] = back_dir;
7214 MovDelay[x][y] = 16 + 16 * RND(3);
7216 else if (element == EL_PACMAN)
7218 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7219 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7221 if (can_turn_left && can_turn_right)
7222 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7223 else if (can_turn_left)
7224 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7225 else if (can_turn_right)
7226 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7228 MovDir[x][y] = back_dir;
7230 MovDelay[x][y] = 6 + RND(40);
7232 else if (element == EL_PIG)
7234 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7235 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7236 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7237 boolean should_turn_left, should_turn_right, should_move_on;
7239 int rnd = RND(rnd_value);
7241 should_turn_left = (can_turn_left &&
7243 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7244 y + back_dy + left_dy)));
7245 should_turn_right = (can_turn_right &&
7247 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7248 y + back_dy + right_dy)));
7249 should_move_on = (can_move_on &&
7252 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7253 y + move_dy + left_dy) ||
7254 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7255 y + move_dy + right_dy)));
7257 if (should_turn_left || should_turn_right || should_move_on)
7259 if (should_turn_left && should_turn_right && should_move_on)
7260 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
7261 rnd < 2 * rnd_value / 3 ? right_dir :
7263 else if (should_turn_left && should_turn_right)
7264 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7265 else if (should_turn_left && should_move_on)
7266 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7267 else if (should_turn_right && should_move_on)
7268 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7269 else if (should_turn_left)
7270 MovDir[x][y] = left_dir;
7271 else if (should_turn_right)
7272 MovDir[x][y] = right_dir;
7273 else if (should_move_on)
7274 MovDir[x][y] = old_move_dir;
7276 else if (can_move_on && rnd > rnd_value / 8)
7277 MovDir[x][y] = old_move_dir;
7278 else if (can_turn_left && can_turn_right)
7279 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7280 else if (can_turn_left && rnd > rnd_value / 8)
7281 MovDir[x][y] = left_dir;
7282 else if (can_turn_right && rnd > rnd_value/8)
7283 MovDir[x][y] = right_dir;
7285 MovDir[x][y] = back_dir;
7287 xx = x + move_xy[MovDir[x][y]].dx;
7288 yy = y + move_xy[MovDir[x][y]].dy;
7290 if (!IN_LEV_FIELD(xx, yy) ||
7291 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7292 MovDir[x][y] = old_move_dir;
7296 else if (element == EL_DRAGON)
7298 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7299 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7300 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7302 int rnd = RND(rnd_value);
7304 if (can_move_on && rnd > rnd_value / 8)
7305 MovDir[x][y] = old_move_dir;
7306 else if (can_turn_left && can_turn_right)
7307 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7308 else if (can_turn_left && rnd > rnd_value / 8)
7309 MovDir[x][y] = left_dir;
7310 else if (can_turn_right && rnd > rnd_value / 8)
7311 MovDir[x][y] = right_dir;
7313 MovDir[x][y] = back_dir;
7315 xx = x + move_xy[MovDir[x][y]].dx;
7316 yy = y + move_xy[MovDir[x][y]].dy;
7318 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7319 MovDir[x][y] = old_move_dir;
7323 else if (element == EL_MOLE)
7325 boolean can_move_on =
7326 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7327 IS_AMOEBOID(Feld[move_x][move_y]) ||
7328 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7331 boolean can_turn_left =
7332 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7333 IS_AMOEBOID(Feld[left_x][left_y])));
7335 boolean can_turn_right =
7336 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7337 IS_AMOEBOID(Feld[right_x][right_y])));
7339 if (can_turn_left && can_turn_right)
7340 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7341 else if (can_turn_left)
7342 MovDir[x][y] = left_dir;
7344 MovDir[x][y] = right_dir;
7347 if (MovDir[x][y] != old_move_dir)
7350 else if (element == EL_BALLOON)
7352 MovDir[x][y] = game.wind_direction;
7355 else if (element == EL_SPRING)
7357 #if USE_NEW_SPRING_BUMPER
7358 if (MovDir[x][y] & MV_HORIZONTAL)
7360 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7361 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7363 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7364 ResetGfxAnimation(move_x, move_y);
7365 TEST_DrawLevelField(move_x, move_y);
7367 MovDir[x][y] = back_dir;
7369 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7370 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7371 MovDir[x][y] = MV_NONE;
7374 if (MovDir[x][y] & MV_HORIZONTAL &&
7375 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7376 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7377 MovDir[x][y] = MV_NONE;
7382 else if (element == EL_ROBOT ||
7383 element == EL_SATELLITE ||
7384 element == EL_PENGUIN ||
7385 element == EL_EMC_ANDROID)
7387 int attr_x = -1, attr_y = -1;
7398 for (i = 0; i < MAX_PLAYERS; i++)
7400 struct PlayerInfo *player = &stored_player[i];
7401 int jx = player->jx, jy = player->jy;
7403 if (!player->active)
7407 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7415 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7416 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7417 game.engine_version < VERSION_IDENT(3,1,0,0)))
7423 if (element == EL_PENGUIN)
7426 static int xy[4][2] =
7434 for (i = 0; i < NUM_DIRECTIONS; i++)
7436 int ex = x + xy[i][0];
7437 int ey = y + xy[i][1];
7439 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7440 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7441 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7442 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7451 MovDir[x][y] = MV_NONE;
7453 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7454 else if (attr_x > x)
7455 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7457 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7458 else if (attr_y > y)
7459 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7461 if (element == EL_ROBOT)
7465 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7466 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7467 Moving2Blocked(x, y, &newx, &newy);
7469 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7470 MovDelay[x][y] = 8 + 8 * !RND(3);
7472 MovDelay[x][y] = 16;
7474 else if (element == EL_PENGUIN)
7480 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7482 boolean first_horiz = RND(2);
7483 int new_move_dir = MovDir[x][y];
7486 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7487 Moving2Blocked(x, y, &newx, &newy);
7489 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7493 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7494 Moving2Blocked(x, y, &newx, &newy);
7496 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7499 MovDir[x][y] = old_move_dir;
7503 else if (element == EL_SATELLITE)
7509 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7511 boolean first_horiz = RND(2);
7512 int new_move_dir = MovDir[x][y];
7515 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7516 Moving2Blocked(x, y, &newx, &newy);
7518 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7522 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7523 Moving2Blocked(x, y, &newx, &newy);
7525 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7528 MovDir[x][y] = old_move_dir;
7532 else if (element == EL_EMC_ANDROID)
7534 static int check_pos[16] =
7536 -1, /* 0 => (invalid) */
7537 7, /* 1 => MV_LEFT */
7538 3, /* 2 => MV_RIGHT */
7539 -1, /* 3 => (invalid) */
7541 0, /* 5 => MV_LEFT | MV_UP */
7542 2, /* 6 => MV_RIGHT | MV_UP */
7543 -1, /* 7 => (invalid) */
7544 5, /* 8 => MV_DOWN */
7545 6, /* 9 => MV_LEFT | MV_DOWN */
7546 4, /* 10 => MV_RIGHT | MV_DOWN */
7547 -1, /* 11 => (invalid) */
7548 -1, /* 12 => (invalid) */
7549 -1, /* 13 => (invalid) */
7550 -1, /* 14 => (invalid) */
7551 -1, /* 15 => (invalid) */
7559 { -1, -1, MV_LEFT | MV_UP },
7561 { +1, -1, MV_RIGHT | MV_UP },
7562 { +1, 0, MV_RIGHT },
7563 { +1, +1, MV_RIGHT | MV_DOWN },
7565 { -1, +1, MV_LEFT | MV_DOWN },
7568 int start_pos, check_order;
7569 boolean can_clone = FALSE;
7572 /* check if there is any free field around current position */
7573 for (i = 0; i < 8; i++)
7575 int newx = x + check_xy[i].dx;
7576 int newy = y + check_xy[i].dy;
7578 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7586 if (can_clone) /* randomly find an element to clone */
7590 start_pos = check_pos[RND(8)];
7591 check_order = (RND(2) ? -1 : +1);
7593 for (i = 0; i < 8; i++)
7595 int pos_raw = start_pos + i * check_order;
7596 int pos = (pos_raw + 8) % 8;
7597 int newx = x + check_xy[pos].dx;
7598 int newy = y + check_xy[pos].dy;
7600 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7602 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7603 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7605 Store[x][y] = Feld[newx][newy];
7614 if (can_clone) /* randomly find a direction to move */
7618 start_pos = check_pos[RND(8)];
7619 check_order = (RND(2) ? -1 : +1);
7621 for (i = 0; i < 8; i++)
7623 int pos_raw = start_pos + i * check_order;
7624 int pos = (pos_raw + 8) % 8;
7625 int newx = x + check_xy[pos].dx;
7626 int newy = y + check_xy[pos].dy;
7627 int new_move_dir = check_xy[pos].dir;
7629 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7631 MovDir[x][y] = new_move_dir;
7632 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7641 if (can_clone) /* cloning and moving successful */
7644 /* cannot clone -- try to move towards player */
7646 start_pos = check_pos[MovDir[x][y] & 0x0f];
7647 check_order = (RND(2) ? -1 : +1);
7649 for (i = 0; i < 3; i++)
7651 /* first check start_pos, then previous/next or (next/previous) pos */
7652 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7653 int pos = (pos_raw + 8) % 8;
7654 int newx = x + check_xy[pos].dx;
7655 int newy = y + check_xy[pos].dy;
7656 int new_move_dir = check_xy[pos].dir;
7658 if (IS_PLAYER(newx, newy))
7661 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7663 MovDir[x][y] = new_move_dir;
7664 MovDelay[x][y] = level.android_move_time * 8 + 1;
7671 else if (move_pattern == MV_TURNING_LEFT ||
7672 move_pattern == MV_TURNING_RIGHT ||
7673 move_pattern == MV_TURNING_LEFT_RIGHT ||
7674 move_pattern == MV_TURNING_RIGHT_LEFT ||
7675 move_pattern == MV_TURNING_RANDOM ||
7676 move_pattern == MV_ALL_DIRECTIONS)
7678 boolean can_turn_left =
7679 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7680 boolean can_turn_right =
7681 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7683 if (element_info[element].move_stepsize == 0) /* "not moving" */
7686 if (move_pattern == MV_TURNING_LEFT)
7687 MovDir[x][y] = left_dir;
7688 else if (move_pattern == MV_TURNING_RIGHT)
7689 MovDir[x][y] = right_dir;
7690 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7691 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7692 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7693 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7694 else if (move_pattern == MV_TURNING_RANDOM)
7695 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7696 can_turn_right && !can_turn_left ? right_dir :
7697 RND(2) ? left_dir : right_dir);
7698 else if (can_turn_left && can_turn_right)
7699 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7700 else if (can_turn_left)
7701 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7702 else if (can_turn_right)
7703 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7705 MovDir[x][y] = back_dir;
7707 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7709 else if (move_pattern == MV_HORIZONTAL ||
7710 move_pattern == MV_VERTICAL)
7712 if (move_pattern & old_move_dir)
7713 MovDir[x][y] = back_dir;
7714 else if (move_pattern == MV_HORIZONTAL)
7715 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7716 else if (move_pattern == MV_VERTICAL)
7717 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7719 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7721 else if (move_pattern & MV_ANY_DIRECTION)
7723 MovDir[x][y] = move_pattern;
7724 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7726 else if (move_pattern & MV_WIND_DIRECTION)
7728 MovDir[x][y] = game.wind_direction;
7729 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7731 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7733 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7734 MovDir[x][y] = left_dir;
7735 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7736 MovDir[x][y] = right_dir;
7738 if (MovDir[x][y] != old_move_dir)
7739 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7741 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7743 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7744 MovDir[x][y] = right_dir;
7745 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7746 MovDir[x][y] = left_dir;
7748 if (MovDir[x][y] != old_move_dir)
7749 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7751 else if (move_pattern == MV_TOWARDS_PLAYER ||
7752 move_pattern == MV_AWAY_FROM_PLAYER)
7754 int attr_x = -1, attr_y = -1;
7756 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7767 for (i = 0; i < MAX_PLAYERS; i++)
7769 struct PlayerInfo *player = &stored_player[i];
7770 int jx = player->jx, jy = player->jy;
7772 if (!player->active)
7776 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7784 MovDir[x][y] = MV_NONE;
7786 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7787 else if (attr_x > x)
7788 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7790 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7791 else if (attr_y > y)
7792 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7794 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7796 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7798 boolean first_horiz = RND(2);
7799 int new_move_dir = MovDir[x][y];
7801 if (element_info[element].move_stepsize == 0) /* "not moving" */
7803 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7804 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7810 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7811 Moving2Blocked(x, y, &newx, &newy);
7813 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7817 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7818 Moving2Blocked(x, y, &newx, &newy);
7820 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7823 MovDir[x][y] = old_move_dir;
7826 else if (move_pattern == MV_WHEN_PUSHED ||
7827 move_pattern == MV_WHEN_DROPPED)
7829 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7830 MovDir[x][y] = MV_NONE;
7834 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7836 static int test_xy[7][2] =
7846 static int test_dir[7] =
7856 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7857 int move_preference = -1000000; /* start with very low preference */
7858 int new_move_dir = MV_NONE;
7859 int start_test = RND(4);
7862 for (i = 0; i < NUM_DIRECTIONS; i++)
7864 int move_dir = test_dir[start_test + i];
7865 int move_dir_preference;
7867 xx = x + test_xy[start_test + i][0];
7868 yy = y + test_xy[start_test + i][1];
7870 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7871 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7873 new_move_dir = move_dir;
7878 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7881 move_dir_preference = -1 * RunnerVisit[xx][yy];
7882 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7883 move_dir_preference = PlayerVisit[xx][yy];
7885 if (move_dir_preference > move_preference)
7887 /* prefer field that has not been visited for the longest time */
7888 move_preference = move_dir_preference;
7889 new_move_dir = move_dir;
7891 else if (move_dir_preference == move_preference &&
7892 move_dir == old_move_dir)
7894 /* prefer last direction when all directions are preferred equally */
7895 move_preference = move_dir_preference;
7896 new_move_dir = move_dir;
7900 MovDir[x][y] = new_move_dir;
7901 if (old_move_dir != new_move_dir)
7902 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7906 static void TurnRound(int x, int y)
7908 int direction = MovDir[x][y];
7912 GfxDir[x][y] = MovDir[x][y];
7914 if (direction != MovDir[x][y])
7918 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7920 ResetGfxFrame(x, y, FALSE);
7923 static boolean JustBeingPushed(int x, int y)
7927 for (i = 0; i < MAX_PLAYERS; i++)
7929 struct PlayerInfo *player = &stored_player[i];
7931 if (player->active && player->is_pushing && player->MovPos)
7933 int next_jx = player->jx + (player->jx - player->last_jx);
7934 int next_jy = player->jy + (player->jy - player->last_jy);
7936 if (x == next_jx && y == next_jy)
7944 void StartMoving(int x, int y)
7946 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7947 int element = Feld[x][y];
7952 if (MovDelay[x][y] == 0)
7953 GfxAction[x][y] = ACTION_DEFAULT;
7955 if (CAN_FALL(element) && y < lev_fieldy - 1)
7957 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7958 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7959 if (JustBeingPushed(x, y))
7962 if (element == EL_QUICKSAND_FULL)
7964 if (IS_FREE(x, y + 1))
7966 InitMovingField(x, y, MV_DOWN);
7967 started_moving = TRUE;
7969 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7970 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7971 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7972 Store[x][y] = EL_ROCK;
7974 Store[x][y] = EL_ROCK;
7977 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7979 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7981 if (!MovDelay[x][y])
7983 MovDelay[x][y] = TILEY + 1;
7985 ResetGfxAnimation(x, y);
7986 ResetGfxAnimation(x, y + 1);
7991 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7992 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7999 Feld[x][y] = EL_QUICKSAND_EMPTY;
8000 Feld[x][y + 1] = EL_QUICKSAND_FULL;
8001 Store[x][y + 1] = Store[x][y];
8004 PlayLevelSoundAction(x, y, ACTION_FILLING);
8006 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8008 if (!MovDelay[x][y])
8010 MovDelay[x][y] = TILEY + 1;
8012 ResetGfxAnimation(x, y);
8013 ResetGfxAnimation(x, y + 1);
8018 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
8019 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8026 Feld[x][y] = EL_QUICKSAND_EMPTY;
8027 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8028 Store[x][y + 1] = Store[x][y];
8031 PlayLevelSoundAction(x, y, ACTION_FILLING);
8034 else if (element == EL_QUICKSAND_FAST_FULL)
8036 if (IS_FREE(x, y + 1))
8038 InitMovingField(x, y, MV_DOWN);
8039 started_moving = TRUE;
8041 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
8042 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8043 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8044 Store[x][y] = EL_ROCK;
8046 Store[x][y] = EL_ROCK;
8049 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8051 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8053 if (!MovDelay[x][y])
8055 MovDelay[x][y] = TILEY + 1;
8057 ResetGfxAnimation(x, y);
8058 ResetGfxAnimation(x, y + 1);
8063 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8064 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8071 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8072 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8073 Store[x][y + 1] = Store[x][y];
8076 PlayLevelSoundAction(x, y, ACTION_FILLING);
8078 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8080 if (!MovDelay[x][y])
8082 MovDelay[x][y] = TILEY + 1;
8084 ResetGfxAnimation(x, y);
8085 ResetGfxAnimation(x, y + 1);
8090 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8091 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8098 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8099 Feld[x][y + 1] = EL_QUICKSAND_FULL;
8100 Store[x][y + 1] = Store[x][y];
8103 PlayLevelSoundAction(x, y, ACTION_FILLING);
8106 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8107 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8109 InitMovingField(x, y, MV_DOWN);
8110 started_moving = TRUE;
8112 Feld[x][y] = EL_QUICKSAND_FILLING;
8113 Store[x][y] = element;
8115 PlayLevelSoundAction(x, y, ACTION_FILLING);
8117 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8118 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8120 InitMovingField(x, y, MV_DOWN);
8121 started_moving = TRUE;
8123 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
8124 Store[x][y] = element;
8126 PlayLevelSoundAction(x, y, ACTION_FILLING);
8128 else if (element == EL_MAGIC_WALL_FULL)
8130 if (IS_FREE(x, y + 1))
8132 InitMovingField(x, y, MV_DOWN);
8133 started_moving = TRUE;
8135 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
8136 Store[x][y] = EL_CHANGED(Store[x][y]);
8138 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8140 if (!MovDelay[x][y])
8141 MovDelay[x][y] = TILEY/4 + 1;
8150 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
8151 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
8152 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8156 else if (element == EL_BD_MAGIC_WALL_FULL)
8158 if (IS_FREE(x, y + 1))
8160 InitMovingField(x, y, MV_DOWN);
8161 started_moving = TRUE;
8163 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8164 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8166 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8168 if (!MovDelay[x][y])
8169 MovDelay[x][y] = TILEY/4 + 1;
8178 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8179 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8180 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8184 else if (element == EL_DC_MAGIC_WALL_FULL)
8186 if (IS_FREE(x, y + 1))
8188 InitMovingField(x, y, MV_DOWN);
8189 started_moving = TRUE;
8191 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8192 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8194 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8196 if (!MovDelay[x][y])
8197 MovDelay[x][y] = TILEY/4 + 1;
8206 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8207 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8208 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8212 else if ((CAN_PASS_MAGIC_WALL(element) &&
8213 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8214 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8215 (CAN_PASS_DC_MAGIC_WALL(element) &&
8216 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8219 InitMovingField(x, y, MV_DOWN);
8220 started_moving = TRUE;
8223 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8224 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8225 EL_DC_MAGIC_WALL_FILLING);
8226 Store[x][y] = element;
8228 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8230 SplashAcid(x, y + 1);
8232 InitMovingField(x, y, MV_DOWN);
8233 started_moving = TRUE;
8235 Store[x][y] = EL_ACID;
8238 #if USE_FIX_IMPACT_COLLISION
8239 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8240 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8242 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8243 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8245 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8246 CAN_FALL(element) && WasJustFalling[x][y] &&
8247 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8249 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8250 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8251 (Feld[x][y + 1] == EL_BLOCKED)))
8253 /* this is needed for a special case not covered by calling "Impact()"
8254 from "ContinueMoving()": if an element moves to a tile directly below
8255 another element which was just falling on that tile (which was empty
8256 in the previous frame), the falling element above would just stop
8257 instead of smashing the element below (in previous version, the above
8258 element was just checked for "moving" instead of "falling", resulting
8259 in incorrect smashes caused by horizontal movement of the above
8260 element; also, the case of the player being the element to smash was
8261 simply not covered here... :-/ ) */
8263 CheckCollision[x][y] = 0;
8264 CheckImpact[x][y] = 0;
8268 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8270 if (MovDir[x][y] == MV_NONE)
8272 InitMovingField(x, y, MV_DOWN);
8273 started_moving = TRUE;
8276 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8278 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8279 MovDir[x][y] = MV_DOWN;
8281 InitMovingField(x, y, MV_DOWN);
8282 started_moving = TRUE;
8284 else if (element == EL_AMOEBA_DROP)
8286 Feld[x][y] = EL_AMOEBA_GROWING;
8287 Store[x][y] = EL_AMOEBA_WET;
8289 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8290 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8291 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8292 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8294 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
8295 (IS_FREE(x - 1, y + 1) ||
8296 Feld[x - 1][y + 1] == EL_ACID));
8297 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8298 (IS_FREE(x + 1, y + 1) ||
8299 Feld[x + 1][y + 1] == EL_ACID));
8300 boolean can_fall_any = (can_fall_left || can_fall_right);
8301 boolean can_fall_both = (can_fall_left && can_fall_right);
8302 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8304 #if USE_NEW_ALL_SLIPPERY
8305 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8307 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8308 can_fall_right = FALSE;
8309 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8310 can_fall_left = FALSE;
8311 else if (slippery_type == SLIPPERY_ONLY_LEFT)
8312 can_fall_right = FALSE;
8313 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8314 can_fall_left = FALSE;
8316 can_fall_any = (can_fall_left || can_fall_right);
8317 can_fall_both = FALSE;
8320 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8322 if (slippery_type == SLIPPERY_ONLY_LEFT)
8323 can_fall_right = FALSE;
8324 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8325 can_fall_left = FALSE;
8326 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8327 can_fall_right = FALSE;
8328 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8329 can_fall_left = FALSE;
8331 can_fall_any = (can_fall_left || can_fall_right);
8332 can_fall_both = (can_fall_left && can_fall_right);
8336 #if USE_NEW_ALL_SLIPPERY
8338 #if USE_NEW_SP_SLIPPERY
8339 /* !!! better use the same properties as for custom elements here !!! */
8340 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8341 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8343 can_fall_right = FALSE; /* slip down on left side */
8344 can_fall_both = FALSE;
8349 #if USE_NEW_ALL_SLIPPERY
8352 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8353 can_fall_right = FALSE; /* slip down on left side */
8355 can_fall_left = !(can_fall_right = RND(2));
8357 can_fall_both = FALSE;
8362 if (game.emulation == EMU_BOULDERDASH ||
8363 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8364 can_fall_right = FALSE; /* slip down on left side */
8366 can_fall_left = !(can_fall_right = RND(2));
8368 can_fall_both = FALSE;
8374 /* if not determined otherwise, prefer left side for slipping down */
8375 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8376 started_moving = TRUE;
8380 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8382 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8385 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
8386 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8387 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8388 int belt_dir = game.belt_dir[belt_nr];
8390 if ((belt_dir == MV_LEFT && left_is_free) ||
8391 (belt_dir == MV_RIGHT && right_is_free))
8393 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8395 InitMovingField(x, y, belt_dir);
8396 started_moving = TRUE;
8398 Pushed[x][y] = TRUE;
8399 Pushed[nextx][y] = TRUE;
8401 GfxAction[x][y] = ACTION_DEFAULT;
8405 MovDir[x][y] = 0; /* if element was moving, stop it */
8410 /* not "else if" because of elements that can fall and move (EL_SPRING) */
8412 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8414 if (CAN_MOVE(element) && !started_moving)
8417 int move_pattern = element_info[element].move_pattern;
8422 if (MovDir[x][y] == MV_NONE)
8424 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8425 x, y, element, element_info[element].token_name);
8426 printf("StartMoving(): This should never happen!\n");
8431 Moving2Blocked(x, y, &newx, &newy);
8433 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8436 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8437 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8439 WasJustMoving[x][y] = 0;
8440 CheckCollision[x][y] = 0;
8442 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8444 if (Feld[x][y] != element) /* element has changed */
8448 if (!MovDelay[x][y]) /* start new movement phase */
8450 /* all objects that can change their move direction after each step
8451 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8453 if (element != EL_YAMYAM &&
8454 element != EL_DARK_YAMYAM &&
8455 element != EL_PACMAN &&
8456 !(move_pattern & MV_ANY_DIRECTION) &&
8457 move_pattern != MV_TURNING_LEFT &&
8458 move_pattern != MV_TURNING_RIGHT &&
8459 move_pattern != MV_TURNING_LEFT_RIGHT &&
8460 move_pattern != MV_TURNING_RIGHT_LEFT &&
8461 move_pattern != MV_TURNING_RANDOM)
8465 if (MovDelay[x][y] && (element == EL_BUG ||
8466 element == EL_SPACESHIP ||
8467 element == EL_SP_SNIKSNAK ||
8468 element == EL_SP_ELECTRON ||
8469 element == EL_MOLE))
8470 TEST_DrawLevelField(x, y);
8474 if (MovDelay[x][y]) /* wait some time before next movement */
8478 if (element == EL_ROBOT ||
8479 element == EL_YAMYAM ||
8480 element == EL_DARK_YAMYAM)
8482 DrawLevelElementAnimationIfNeeded(x, y, element);
8483 PlayLevelSoundAction(x, y, ACTION_WAITING);
8485 else if (element == EL_SP_ELECTRON)
8486 DrawLevelElementAnimationIfNeeded(x, y, element);
8487 else if (element == EL_DRAGON)
8490 int dir = MovDir[x][y];
8491 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8492 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
8493 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
8494 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
8495 dir == MV_UP ? IMG_FLAMES_1_UP :
8496 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8497 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8499 GfxAction[x][y] = ACTION_ATTACKING;
8501 if (IS_PLAYER(x, y))
8502 DrawPlayerField(x, y);
8504 TEST_DrawLevelField(x, y);
8506 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8508 for (i = 1; i <= 3; i++)
8510 int xx = x + i * dx;
8511 int yy = y + i * dy;
8512 int sx = SCREENX(xx);
8513 int sy = SCREENY(yy);
8514 int flame_graphic = graphic + (i - 1);
8516 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8521 int flamed = MovingOrBlocked2Element(xx, yy);
8525 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8527 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8528 RemoveMovingField(xx, yy);
8530 RemoveField(xx, yy);
8532 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8535 RemoveMovingField(xx, yy);
8538 ChangeDelay[xx][yy] = 0;
8540 Feld[xx][yy] = EL_FLAMES;
8542 if (IN_SCR_FIELD(sx, sy))
8544 TEST_DrawLevelFieldCrumbled(xx, yy);
8545 DrawGraphic(sx, sy, flame_graphic, frame);
8550 if (Feld[xx][yy] == EL_FLAMES)
8551 Feld[xx][yy] = EL_EMPTY;
8552 TEST_DrawLevelField(xx, yy);
8557 if (MovDelay[x][y]) /* element still has to wait some time */
8559 PlayLevelSoundAction(x, y, ACTION_WAITING);
8565 /* now make next step */
8567 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8569 if (DONT_COLLIDE_WITH(element) &&
8570 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8571 !PLAYER_ENEMY_PROTECTED(newx, newy))
8573 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8578 else if (CAN_MOVE_INTO_ACID(element) &&
8579 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8580 !IS_MV_DIAGONAL(MovDir[x][y]) &&
8581 (MovDir[x][y] == MV_DOWN ||
8582 game.engine_version >= VERSION_IDENT(3,1,0,0)))
8584 SplashAcid(newx, newy);
8585 Store[x][y] = EL_ACID;
8587 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8589 if (Feld[newx][newy] == EL_EXIT_OPEN ||
8590 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8591 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8592 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8595 TEST_DrawLevelField(x, y);
8597 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8598 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8599 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8601 local_player->friends_still_needed--;
8602 if (!local_player->friends_still_needed &&
8603 !local_player->GameOver && AllPlayersGone)
8604 PlayerWins(local_player);
8608 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8610 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8611 TEST_DrawLevelField(newx, newy);
8613 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8615 else if (!IS_FREE(newx, newy))
8617 GfxAction[x][y] = ACTION_WAITING;
8619 if (IS_PLAYER(x, y))
8620 DrawPlayerField(x, y);
8622 TEST_DrawLevelField(x, y);
8627 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8629 if (IS_FOOD_PIG(Feld[newx][newy]))
8631 if (IS_MOVING(newx, newy))
8632 RemoveMovingField(newx, newy);
8635 Feld[newx][newy] = EL_EMPTY;
8636 TEST_DrawLevelField(newx, newy);
8639 PlayLevelSound(x, y, SND_PIG_DIGGING);
8641 else if (!IS_FREE(newx, newy))
8643 if (IS_PLAYER(x, y))
8644 DrawPlayerField(x, y);
8646 TEST_DrawLevelField(x, y);
8651 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8653 if (Store[x][y] != EL_EMPTY)
8655 boolean can_clone = FALSE;
8658 /* check if element to clone is still there */
8659 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8661 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8669 /* cannot clone or target field not free anymore -- do not clone */
8670 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8671 Store[x][y] = EL_EMPTY;
8674 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8676 if (IS_MV_DIAGONAL(MovDir[x][y]))
8678 int diagonal_move_dir = MovDir[x][y];
8679 int stored = Store[x][y];
8680 int change_delay = 8;
8683 /* android is moving diagonally */
8685 CreateField(x, y, EL_DIAGONAL_SHRINKING);
8687 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8688 GfxElement[x][y] = EL_EMC_ANDROID;
8689 GfxAction[x][y] = ACTION_SHRINKING;
8690 GfxDir[x][y] = diagonal_move_dir;
8691 ChangeDelay[x][y] = change_delay;
8693 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8696 DrawLevelGraphicAnimation(x, y, graphic);
8697 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8699 if (Feld[newx][newy] == EL_ACID)
8701 SplashAcid(newx, newy);
8706 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8708 Store[newx][newy] = EL_EMC_ANDROID;
8709 GfxElement[newx][newy] = EL_EMC_ANDROID;
8710 GfxAction[newx][newy] = ACTION_GROWING;
8711 GfxDir[newx][newy] = diagonal_move_dir;
8712 ChangeDelay[newx][newy] = change_delay;
8714 graphic = el_act_dir2img(GfxElement[newx][newy],
8715 GfxAction[newx][newy], GfxDir[newx][newy]);
8717 DrawLevelGraphicAnimation(newx, newy, graphic);
8718 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8724 Feld[newx][newy] = EL_EMPTY;
8725 TEST_DrawLevelField(newx, newy);
8727 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8730 else if (!IS_FREE(newx, newy))
8733 if (IS_PLAYER(x, y))
8734 DrawPlayerField(x, y);
8736 TEST_DrawLevelField(x, y);
8742 else if (IS_CUSTOM_ELEMENT(element) &&
8743 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8746 if (!DigFieldByCE(newx, newy, element))
8749 int new_element = Feld[newx][newy];
8751 if (!IS_FREE(newx, newy))
8753 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8754 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8757 /* no element can dig solid indestructible elements */
8758 if (IS_INDESTRUCTIBLE(new_element) &&
8759 !IS_DIGGABLE(new_element) &&
8760 !IS_COLLECTIBLE(new_element))
8763 if (AmoebaNr[newx][newy] &&
8764 (new_element == EL_AMOEBA_FULL ||
8765 new_element == EL_BD_AMOEBA ||
8766 new_element == EL_AMOEBA_GROWING))
8768 AmoebaCnt[AmoebaNr[newx][newy]]--;
8769 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8772 if (IS_MOVING(newx, newy))
8773 RemoveMovingField(newx, newy);
8776 RemoveField(newx, newy);
8777 TEST_DrawLevelField(newx, newy);
8780 /* if digged element was about to explode, prevent the explosion */
8781 ExplodeField[newx][newy] = EX_TYPE_NONE;
8783 PlayLevelSoundAction(x, y, action);
8786 Store[newx][newy] = EL_EMPTY;
8789 /* this makes it possible to leave the removed element again */
8790 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8791 Store[newx][newy] = new_element;
8793 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8795 int move_leave_element = element_info[element].move_leave_element;
8797 /* this makes it possible to leave the removed element again */
8798 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8799 new_element : move_leave_element);
8805 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8807 RunnerVisit[x][y] = FrameCounter;
8808 PlayerVisit[x][y] /= 8; /* expire player visit path */
8811 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8813 if (!IS_FREE(newx, newy))
8815 if (IS_PLAYER(x, y))
8816 DrawPlayerField(x, y);
8818 TEST_DrawLevelField(x, y);
8824 boolean wanna_flame = !RND(10);
8825 int dx = newx - x, dy = newy - y;
8826 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8827 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8828 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8829 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8830 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8831 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8834 IS_CLASSIC_ENEMY(element1) ||
8835 IS_CLASSIC_ENEMY(element2)) &&
8836 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8837 element1 != EL_FLAMES && element2 != EL_FLAMES)
8839 ResetGfxAnimation(x, y);
8840 GfxAction[x][y] = ACTION_ATTACKING;
8842 if (IS_PLAYER(x, y))
8843 DrawPlayerField(x, y);
8845 TEST_DrawLevelField(x, y);
8847 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8849 MovDelay[x][y] = 50;
8853 RemoveField(newx, newy);
8855 Feld[newx][newy] = EL_FLAMES;
8856 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8859 RemoveField(newx1, newy1);
8861 Feld[newx1][newy1] = EL_FLAMES;
8863 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8866 RemoveField(newx2, newy2);
8868 Feld[newx2][newy2] = EL_FLAMES;
8875 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8876 Feld[newx][newy] == EL_DIAMOND)
8878 if (IS_MOVING(newx, newy))
8879 RemoveMovingField(newx, newy);
8882 Feld[newx][newy] = EL_EMPTY;
8883 TEST_DrawLevelField(newx, newy);
8886 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8888 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8889 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8891 if (AmoebaNr[newx][newy])
8893 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8894 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8895 Feld[newx][newy] == EL_BD_AMOEBA)
8896 AmoebaCnt[AmoebaNr[newx][newy]]--;
8901 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8903 RemoveMovingField(newx, newy);
8906 if (IS_MOVING(newx, newy))
8908 RemoveMovingField(newx, newy);
8913 Feld[newx][newy] = EL_EMPTY;
8914 TEST_DrawLevelField(newx, newy);
8917 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8919 else if ((element == EL_PACMAN || element == EL_MOLE)
8920 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8922 if (AmoebaNr[newx][newy])
8924 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8925 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8926 Feld[newx][newy] == EL_BD_AMOEBA)
8927 AmoebaCnt[AmoebaNr[newx][newy]]--;
8930 if (element == EL_MOLE)
8932 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8933 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8935 ResetGfxAnimation(x, y);
8936 GfxAction[x][y] = ACTION_DIGGING;
8937 TEST_DrawLevelField(x, y);
8939 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8941 return; /* wait for shrinking amoeba */
8943 else /* element == EL_PACMAN */
8945 Feld[newx][newy] = EL_EMPTY;
8946 TEST_DrawLevelField(newx, newy);
8947 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8950 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8951 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8952 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8954 /* wait for shrinking amoeba to completely disappear */
8957 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8959 /* object was running against a wall */
8964 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8965 if (move_pattern & MV_ANY_DIRECTION &&
8966 move_pattern == MovDir[x][y])
8968 int blocking_element =
8969 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8971 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8974 element = Feld[x][y]; /* element might have changed */
8978 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8979 DrawLevelElementAnimation(x, y, element);
8981 if (DONT_TOUCH(element))
8982 TestIfBadThingTouchesPlayer(x, y);
8987 InitMovingField(x, y, MovDir[x][y]);
8989 PlayLevelSoundAction(x, y, ACTION_MOVING);
8993 ContinueMoving(x, y);
8996 void ContinueMoving(int x, int y)
8998 int element = Feld[x][y];
8999 struct ElementInfo *ei = &element_info[element];
9000 int direction = MovDir[x][y];
9001 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9002 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9003 int newx = x + dx, newy = y + dy;
9004 int stored = Store[x][y];
9005 int stored_new = Store[newx][newy];
9006 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
9007 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
9008 boolean last_line = (newy == lev_fieldy - 1);
9010 MovPos[x][y] += getElementMoveStepsize(x, y);
9012 if (pushed_by_player) /* special case: moving object pushed by player */
9013 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
9015 if (ABS(MovPos[x][y]) < TILEX)
9018 int ee = Feld[x][y];
9019 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9020 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
9022 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
9023 x, y, ABS(MovPos[x][y]),
9025 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
9028 TEST_DrawLevelField(x, y);
9030 return; /* element is still moving */
9033 /* element reached destination field */
9035 Feld[x][y] = EL_EMPTY;
9036 Feld[newx][newy] = element;
9037 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
9039 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
9041 element = Feld[newx][newy] = EL_ACID;
9043 else if (element == EL_MOLE)
9045 Feld[x][y] = EL_SAND;
9047 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9049 else if (element == EL_QUICKSAND_FILLING)
9051 element = Feld[newx][newy] = get_next_element(element);
9052 Store[newx][newy] = Store[x][y];
9054 else if (element == EL_QUICKSAND_EMPTYING)
9056 Feld[x][y] = get_next_element(element);
9057 element = Feld[newx][newy] = Store[x][y];
9059 else if (element == EL_QUICKSAND_FAST_FILLING)
9061 element = Feld[newx][newy] = get_next_element(element);
9062 Store[newx][newy] = Store[x][y];
9064 else if (element == EL_QUICKSAND_FAST_EMPTYING)
9066 Feld[x][y] = get_next_element(element);
9067 element = Feld[newx][newy] = Store[x][y];
9069 else if (element == EL_MAGIC_WALL_FILLING)
9071 element = Feld[newx][newy] = get_next_element(element);
9072 if (!game.magic_wall_active)
9073 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
9074 Store[newx][newy] = Store[x][y];
9076 else if (element == EL_MAGIC_WALL_EMPTYING)
9078 Feld[x][y] = get_next_element(element);
9079 if (!game.magic_wall_active)
9080 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9081 element = Feld[newx][newy] = Store[x][y];
9083 #if USE_NEW_CUSTOM_VALUE
9084 InitField(newx, newy, FALSE);
9087 else if (element == EL_BD_MAGIC_WALL_FILLING)
9089 element = Feld[newx][newy] = get_next_element(element);
9090 if (!game.magic_wall_active)
9091 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
9092 Store[newx][newy] = Store[x][y];
9094 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
9096 Feld[x][y] = get_next_element(element);
9097 if (!game.magic_wall_active)
9098 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9099 element = Feld[newx][newy] = Store[x][y];
9101 #if USE_NEW_CUSTOM_VALUE
9102 InitField(newx, newy, FALSE);
9105 else if (element == EL_DC_MAGIC_WALL_FILLING)
9107 element = Feld[newx][newy] = get_next_element(element);
9108 if (!game.magic_wall_active)
9109 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
9110 Store[newx][newy] = Store[x][y];
9112 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
9114 Feld[x][y] = get_next_element(element);
9115 if (!game.magic_wall_active)
9116 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
9117 element = Feld[newx][newy] = Store[x][y];
9119 #if USE_NEW_CUSTOM_VALUE
9120 InitField(newx, newy, FALSE);
9123 else if (element == EL_AMOEBA_DROPPING)
9125 Feld[x][y] = get_next_element(element);
9126 element = Feld[newx][newy] = Store[x][y];
9128 else if (element == EL_SOKOBAN_OBJECT)
9131 Feld[x][y] = Back[x][y];
9133 if (Back[newx][newy])
9134 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
9136 Back[x][y] = Back[newx][newy] = 0;
9139 Store[x][y] = EL_EMPTY;
9144 MovDelay[newx][newy] = 0;
9146 if (CAN_CHANGE_OR_HAS_ACTION(element))
9148 /* copy element change control values to new field */
9149 ChangeDelay[newx][newy] = ChangeDelay[x][y];
9150 ChangePage[newx][newy] = ChangePage[x][y];
9151 ChangeCount[newx][newy] = ChangeCount[x][y];
9152 ChangeEvent[newx][newy] = ChangeEvent[x][y];
9155 #if USE_NEW_CUSTOM_VALUE
9156 CustomValue[newx][newy] = CustomValue[x][y];
9159 ChangeDelay[x][y] = 0;
9160 ChangePage[x][y] = -1;
9161 ChangeCount[x][y] = 0;
9162 ChangeEvent[x][y] = -1;
9164 #if USE_NEW_CUSTOM_VALUE
9165 CustomValue[x][y] = 0;
9168 /* copy animation control values to new field */
9169 GfxFrame[newx][newy] = GfxFrame[x][y];
9170 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
9171 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
9172 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
9174 Pushed[x][y] = Pushed[newx][newy] = FALSE;
9176 /* some elements can leave other elements behind after moving */
9178 if (ei->move_leave_element != EL_EMPTY &&
9179 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9180 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9182 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
9183 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9184 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9187 int move_leave_element = ei->move_leave_element;
9191 /* this makes it possible to leave the removed element again */
9192 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9193 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9195 /* this makes it possible to leave the removed element again */
9196 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9197 move_leave_element = stored;
9200 /* this makes it possible to leave the removed element again */
9201 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
9202 ei->move_leave_element == EL_TRIGGER_ELEMENT)
9203 move_leave_element = stored;
9206 Feld[x][y] = move_leave_element;
9208 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
9209 MovDir[x][y] = direction;
9211 InitField(x, y, FALSE);
9213 if (GFX_CRUMBLED(Feld[x][y]))
9214 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9216 if (ELEM_IS_PLAYER(move_leave_element))
9217 RelocatePlayer(x, y, move_leave_element);
9220 /* do this after checking for left-behind element */
9221 ResetGfxAnimation(x, y); /* reset animation values for old field */
9223 if (!CAN_MOVE(element) ||
9224 (CAN_FALL(element) && direction == MV_DOWN &&
9225 (element == EL_SPRING ||
9226 element_info[element].move_pattern == MV_WHEN_PUSHED ||
9227 element_info[element].move_pattern == MV_WHEN_DROPPED)))
9228 GfxDir[x][y] = MovDir[newx][newy] = 0;
9230 TEST_DrawLevelField(x, y);
9231 TEST_DrawLevelField(newx, newy);
9233 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
9235 /* prevent pushed element from moving on in pushed direction */
9236 if (pushed_by_player && CAN_MOVE(element) &&
9237 element_info[element].move_pattern & MV_ANY_DIRECTION &&
9238 !(element_info[element].move_pattern & direction))
9239 TurnRound(newx, newy);
9241 /* prevent elements on conveyor belt from moving on in last direction */
9242 if (pushed_by_conveyor && CAN_FALL(element) &&
9243 direction & MV_HORIZONTAL)
9244 MovDir[newx][newy] = 0;
9246 if (!pushed_by_player)
9248 int nextx = newx + dx, nexty = newy + dy;
9249 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9251 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9253 if (CAN_FALL(element) && direction == MV_DOWN)
9254 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9256 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9257 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9259 #if USE_FIX_IMPACT_COLLISION
9260 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9261 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9265 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
9267 TestIfBadThingTouchesPlayer(newx, newy);
9268 TestIfBadThingTouchesFriend(newx, newy);
9270 if (!IS_CUSTOM_ELEMENT(element))
9271 TestIfBadThingTouchesOtherBadThing(newx, newy);
9273 else if (element == EL_PENGUIN)
9274 TestIfFriendTouchesBadThing(newx, newy);
9276 if (DONT_GET_HIT_BY(element))
9278 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9281 /* give the player one last chance (one more frame) to move away */
9282 if (CAN_FALL(element) && direction == MV_DOWN &&
9283 (last_line || (!IS_FREE(x, newy + 1) &&
9284 (!IS_PLAYER(x, newy + 1) ||
9285 game.engine_version < VERSION_IDENT(3,1,1,0)))))
9288 if (pushed_by_player && !game.use_change_when_pushing_bug)
9290 int push_side = MV_DIR_OPPOSITE(direction);
9291 struct PlayerInfo *player = PLAYERINFO(x, y);
9293 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9294 player->index_bit, push_side);
9295 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9296 player->index_bit, push_side);
9299 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
9300 MovDelay[newx][newy] = 1;
9302 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9304 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
9307 if (ChangePage[newx][newy] != -1) /* delayed change */
9309 int page = ChangePage[newx][newy];
9310 struct ElementChangeInfo *change = &ei->change_page[page];
9312 ChangePage[newx][newy] = -1;
9314 if (change->can_change)
9316 if (ChangeElement(newx, newy, element, page))
9318 if (change->post_change_function)
9319 change->post_change_function(newx, newy);
9323 if (change->has_action)
9324 ExecuteCustomElementAction(newx, newy, element, page);
9328 TestIfElementHitsCustomElement(newx, newy, direction);
9329 TestIfPlayerTouchesCustomElement(newx, newy);
9330 TestIfElementTouchesCustomElement(newx, newy);
9332 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9333 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9334 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9335 MV_DIR_OPPOSITE(direction));
9338 int AmoebeNachbarNr(int ax, int ay)
9341 int element = Feld[ax][ay];
9343 static int xy[4][2] =
9351 for (i = 0; i < NUM_DIRECTIONS; i++)
9353 int x = ax + xy[i][0];
9354 int y = ay + xy[i][1];
9356 if (!IN_LEV_FIELD(x, y))
9359 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9360 group_nr = AmoebaNr[x][y];
9366 void AmoebenVereinigen(int ax, int ay)
9368 int i, x, y, xx, yy;
9369 int new_group_nr = AmoebaNr[ax][ay];
9370 static int xy[4][2] =
9378 if (new_group_nr == 0)
9381 for (i = 0; i < NUM_DIRECTIONS; i++)
9386 if (!IN_LEV_FIELD(x, y))
9389 if ((Feld[x][y] == EL_AMOEBA_FULL ||
9390 Feld[x][y] == EL_BD_AMOEBA ||
9391 Feld[x][y] == EL_AMOEBA_DEAD) &&
9392 AmoebaNr[x][y] != new_group_nr)
9394 int old_group_nr = AmoebaNr[x][y];
9396 if (old_group_nr == 0)
9399 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9400 AmoebaCnt[old_group_nr] = 0;
9401 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9402 AmoebaCnt2[old_group_nr] = 0;
9404 SCAN_PLAYFIELD(xx, yy)
9406 if (AmoebaNr[xx][yy] == old_group_nr)
9407 AmoebaNr[xx][yy] = new_group_nr;
9413 void AmoebeUmwandeln(int ax, int ay)
9417 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9419 int group_nr = AmoebaNr[ax][ay];
9424 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9425 printf("AmoebeUmwandeln(): This should never happen!\n");
9430 SCAN_PLAYFIELD(x, y)
9432 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9435 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9439 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9440 SND_AMOEBA_TURNING_TO_GEM :
9441 SND_AMOEBA_TURNING_TO_ROCK));
9446 static int xy[4][2] =
9454 for (i = 0; i < NUM_DIRECTIONS; i++)
9459 if (!IN_LEV_FIELD(x, y))
9462 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9464 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9465 SND_AMOEBA_TURNING_TO_GEM :
9466 SND_AMOEBA_TURNING_TO_ROCK));
9473 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9476 int group_nr = AmoebaNr[ax][ay];
9477 boolean done = FALSE;
9482 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9483 printf("AmoebeUmwandelnBD(): This should never happen!\n");
9488 SCAN_PLAYFIELD(x, y)
9490 if (AmoebaNr[x][y] == group_nr &&
9491 (Feld[x][y] == EL_AMOEBA_DEAD ||
9492 Feld[x][y] == EL_BD_AMOEBA ||
9493 Feld[x][y] == EL_AMOEBA_GROWING))
9496 Feld[x][y] = new_element;
9497 InitField(x, y, FALSE);
9498 TEST_DrawLevelField(x, y);
9504 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9505 SND_BD_AMOEBA_TURNING_TO_ROCK :
9506 SND_BD_AMOEBA_TURNING_TO_GEM));
9509 void AmoebeWaechst(int x, int y)
9511 static unsigned long sound_delay = 0;
9512 static unsigned long sound_delay_value = 0;
9514 if (!MovDelay[x][y]) /* start new growing cycle */
9518 if (DelayReached(&sound_delay, sound_delay_value))
9520 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9521 sound_delay_value = 30;
9525 if (MovDelay[x][y]) /* wait some time before growing bigger */
9528 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9530 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9531 6 - MovDelay[x][y]);
9533 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9536 if (!MovDelay[x][y])
9538 Feld[x][y] = Store[x][y];
9540 TEST_DrawLevelField(x, y);
9545 void AmoebaDisappearing(int x, int y)
9547 static unsigned long sound_delay = 0;
9548 static unsigned long sound_delay_value = 0;
9550 if (!MovDelay[x][y]) /* start new shrinking cycle */
9554 if (DelayReached(&sound_delay, sound_delay_value))
9555 sound_delay_value = 30;
9558 if (MovDelay[x][y]) /* wait some time before shrinking */
9561 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9563 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9564 6 - MovDelay[x][y]);
9566 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9569 if (!MovDelay[x][y])
9571 Feld[x][y] = EL_EMPTY;
9572 TEST_DrawLevelField(x, y);
9574 /* don't let mole enter this field in this cycle;
9575 (give priority to objects falling to this field from above) */
9581 void AmoebeAbleger(int ax, int ay)
9584 int element = Feld[ax][ay];
9585 int graphic = el2img(element);
9586 int newax = ax, neway = ay;
9587 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9588 static int xy[4][2] =
9596 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9598 Feld[ax][ay] = EL_AMOEBA_DEAD;
9599 TEST_DrawLevelField(ax, ay);
9603 if (IS_ANIMATED(graphic))
9604 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9606 if (!MovDelay[ax][ay]) /* start making new amoeba field */
9607 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9609 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
9612 if (MovDelay[ax][ay])
9616 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9619 int x = ax + xy[start][0];
9620 int y = ay + xy[start][1];
9622 if (!IN_LEV_FIELD(x, y))
9625 if (IS_FREE(x, y) ||
9626 CAN_GROW_INTO(Feld[x][y]) ||
9627 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9628 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9634 if (newax == ax && neway == ay)
9637 else /* normal or "filled" (BD style) amoeba */
9640 boolean waiting_for_player = FALSE;
9642 for (i = 0; i < NUM_DIRECTIONS; i++)
9644 int j = (start + i) % 4;
9645 int x = ax + xy[j][0];
9646 int y = ay + xy[j][1];
9648 if (!IN_LEV_FIELD(x, y))
9651 if (IS_FREE(x, y) ||
9652 CAN_GROW_INTO(Feld[x][y]) ||
9653 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9654 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9660 else if (IS_PLAYER(x, y))
9661 waiting_for_player = TRUE;
9664 if (newax == ax && neway == ay) /* amoeba cannot grow */
9666 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9668 Feld[ax][ay] = EL_AMOEBA_DEAD;
9669 TEST_DrawLevelField(ax, ay);
9670 AmoebaCnt[AmoebaNr[ax][ay]]--;
9672 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
9674 if (element == EL_AMOEBA_FULL)
9675 AmoebeUmwandeln(ax, ay);
9676 else if (element == EL_BD_AMOEBA)
9677 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9682 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9684 /* amoeba gets larger by growing in some direction */
9686 int new_group_nr = AmoebaNr[ax][ay];
9689 if (new_group_nr == 0)
9691 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9692 printf("AmoebeAbleger(): This should never happen!\n");
9697 AmoebaNr[newax][neway] = new_group_nr;
9698 AmoebaCnt[new_group_nr]++;
9699 AmoebaCnt2[new_group_nr]++;
9701 /* if amoeba touches other amoeba(s) after growing, unify them */
9702 AmoebenVereinigen(newax, neway);
9704 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9706 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9712 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9713 (neway == lev_fieldy - 1 && newax != ax))
9715 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
9716 Store[newax][neway] = element;
9718 else if (neway == ay || element == EL_EMC_DRIPPER)
9720 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
9722 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9726 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
9727 Feld[ax][ay] = EL_AMOEBA_DROPPING;
9728 Store[ax][ay] = EL_AMOEBA_DROP;
9729 ContinueMoving(ax, ay);
9733 TEST_DrawLevelField(newax, neway);
9736 void Life(int ax, int ay)
9740 int element = Feld[ax][ay];
9741 int graphic = el2img(element);
9742 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9744 boolean changed = FALSE;
9746 if (IS_ANIMATED(graphic))
9747 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9752 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
9753 MovDelay[ax][ay] = life_time;
9755 if (MovDelay[ax][ay]) /* wait some time before next cycle */
9758 if (MovDelay[ax][ay])
9762 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9764 int xx = ax+x1, yy = ay+y1;
9767 if (!IN_LEV_FIELD(xx, yy))
9770 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9772 int x = xx+x2, y = yy+y2;
9774 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9777 if (((Feld[x][y] == element ||
9778 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9780 (IS_FREE(x, y) && Stop[x][y]))
9784 if (xx == ax && yy == ay) /* field in the middle */
9786 if (nachbarn < life_parameter[0] ||
9787 nachbarn > life_parameter[1])
9789 Feld[xx][yy] = EL_EMPTY;
9791 TEST_DrawLevelField(xx, yy);
9792 Stop[xx][yy] = TRUE;
9796 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9797 { /* free border field */
9798 if (nachbarn >= life_parameter[2] &&
9799 nachbarn <= life_parameter[3])
9801 Feld[xx][yy] = element;
9802 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9804 TEST_DrawLevelField(xx, yy);
9805 Stop[xx][yy] = TRUE;
9812 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9813 SND_GAME_OF_LIFE_GROWING);
9816 static void InitRobotWheel(int x, int y)
9818 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9821 static void RunRobotWheel(int x, int y)
9823 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9826 static void StopRobotWheel(int x, int y)
9828 if (ZX == x && ZY == y)
9832 game.robot_wheel_active = FALSE;
9836 static void InitTimegateWheel(int x, int y)
9838 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9841 static void RunTimegateWheel(int x, int y)
9843 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9846 static void InitMagicBallDelay(int x, int y)
9849 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9851 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9855 static void ActivateMagicBall(int bx, int by)
9859 if (level.ball_random)
9861 int pos_border = RND(8); /* select one of the eight border elements */
9862 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9863 int xx = pos_content % 3;
9864 int yy = pos_content / 3;
9869 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9870 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9874 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9876 int xx = x - bx + 1;
9877 int yy = y - by + 1;
9879 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9880 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9884 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9887 void CheckExit(int x, int y)
9889 if (local_player->gems_still_needed > 0 ||
9890 local_player->sokobanfields_still_needed > 0 ||
9891 local_player->lights_still_needed > 0)
9893 int element = Feld[x][y];
9894 int graphic = el2img(element);
9896 if (IS_ANIMATED(graphic))
9897 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9902 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9905 Feld[x][y] = EL_EXIT_OPENING;
9907 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9910 void CheckExitEM(int x, int y)
9912 if (local_player->gems_still_needed > 0 ||
9913 local_player->sokobanfields_still_needed > 0 ||
9914 local_player->lights_still_needed > 0)
9916 int element = Feld[x][y];
9917 int graphic = el2img(element);
9919 if (IS_ANIMATED(graphic))
9920 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9925 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9928 Feld[x][y] = EL_EM_EXIT_OPENING;
9930 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9933 void CheckExitSteel(int x, int y)
9935 if (local_player->gems_still_needed > 0 ||
9936 local_player->sokobanfields_still_needed > 0 ||
9937 local_player->lights_still_needed > 0)
9939 int element = Feld[x][y];
9940 int graphic = el2img(element);
9942 if (IS_ANIMATED(graphic))
9943 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9948 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9951 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9953 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9956 void CheckExitSteelEM(int x, int y)
9958 if (local_player->gems_still_needed > 0 ||
9959 local_player->sokobanfields_still_needed > 0 ||
9960 local_player->lights_still_needed > 0)
9962 int element = Feld[x][y];
9963 int graphic = el2img(element);
9965 if (IS_ANIMATED(graphic))
9966 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9971 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9974 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9976 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9979 void CheckExitSP(int x, int y)
9981 if (local_player->gems_still_needed > 0)
9983 int element = Feld[x][y];
9984 int graphic = el2img(element);
9986 if (IS_ANIMATED(graphic))
9987 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9992 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9995 Feld[x][y] = EL_SP_EXIT_OPENING;
9997 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
10000 static void CloseAllOpenTimegates()
10004 SCAN_PLAYFIELD(x, y)
10006 int element = Feld[x][y];
10008 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
10010 Feld[x][y] = EL_TIMEGATE_CLOSING;
10012 PlayLevelSoundAction(x, y, ACTION_CLOSING);
10017 void DrawTwinkleOnField(int x, int y)
10019 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
10022 if (Feld[x][y] == EL_BD_DIAMOND)
10025 if (MovDelay[x][y] == 0) /* next animation frame */
10026 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
10028 if (MovDelay[x][y] != 0) /* wait some time before next frame */
10032 DrawLevelElementAnimation(x, y, Feld[x][y]);
10034 if (MovDelay[x][y] != 0)
10036 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
10037 10 - MovDelay[x][y]);
10039 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
10044 void MauerWaechst(int x, int y)
10048 if (!MovDelay[x][y]) /* next animation frame */
10049 MovDelay[x][y] = 3 * delay;
10051 if (MovDelay[x][y]) /* wait some time before next frame */
10055 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
10057 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
10058 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
10060 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
10063 if (!MovDelay[x][y])
10065 if (MovDir[x][y] == MV_LEFT)
10067 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
10068 TEST_DrawLevelField(x - 1, y);
10070 else if (MovDir[x][y] == MV_RIGHT)
10072 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
10073 TEST_DrawLevelField(x + 1, y);
10075 else if (MovDir[x][y] == MV_UP)
10077 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
10078 TEST_DrawLevelField(x, y - 1);
10082 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
10083 TEST_DrawLevelField(x, y + 1);
10086 Feld[x][y] = Store[x][y];
10088 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
10089 TEST_DrawLevelField(x, y);
10094 void MauerAbleger(int ax, int ay)
10096 int element = Feld[ax][ay];
10097 int graphic = el2img(element);
10098 boolean oben_frei = FALSE, unten_frei = FALSE;
10099 boolean links_frei = FALSE, rechts_frei = FALSE;
10100 boolean oben_massiv = FALSE, unten_massiv = FALSE;
10101 boolean links_massiv = FALSE, rechts_massiv = FALSE;
10102 boolean new_wall = FALSE;
10104 if (IS_ANIMATED(graphic))
10105 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10107 if (!MovDelay[ax][ay]) /* start building new wall */
10108 MovDelay[ax][ay] = 6;
10110 if (MovDelay[ax][ay]) /* wait some time before building new wall */
10112 MovDelay[ax][ay]--;
10113 if (MovDelay[ax][ay])
10117 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10119 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10121 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10123 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10124 rechts_frei = TRUE;
10126 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
10127 element == EL_EXPANDABLE_WALL_ANY)
10131 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
10132 Store[ax][ay-1] = element;
10133 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10134 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10135 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10136 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
10141 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
10142 Store[ax][ay+1] = element;
10143 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10144 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10145 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10146 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
10151 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10152 element == EL_EXPANDABLE_WALL_ANY ||
10153 element == EL_EXPANDABLE_WALL ||
10154 element == EL_BD_EXPANDABLE_WALL)
10158 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
10159 Store[ax-1][ay] = element;
10160 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10161 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10162 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10163 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
10169 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
10170 Store[ax+1][ay] = element;
10171 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10172 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10173 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10174 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
10179 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
10180 TEST_DrawLevelField(ax, ay);
10182 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10183 oben_massiv = TRUE;
10184 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10185 unten_massiv = TRUE;
10186 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10187 links_massiv = TRUE;
10188 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10189 rechts_massiv = TRUE;
10191 if (((oben_massiv && unten_massiv) ||
10192 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10193 element == EL_EXPANDABLE_WALL) &&
10194 ((links_massiv && rechts_massiv) ||
10195 element == EL_EXPANDABLE_WALL_VERTICAL))
10196 Feld[ax][ay] = EL_WALL;
10199 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10202 void MauerAblegerStahl(int ax, int ay)
10204 int element = Feld[ax][ay];
10205 int graphic = el2img(element);
10206 boolean oben_frei = FALSE, unten_frei = FALSE;
10207 boolean links_frei = FALSE, rechts_frei = FALSE;
10208 boolean oben_massiv = FALSE, unten_massiv = FALSE;
10209 boolean links_massiv = FALSE, rechts_massiv = FALSE;
10210 boolean new_wall = FALSE;
10212 if (IS_ANIMATED(graphic))
10213 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10215 if (!MovDelay[ax][ay]) /* start building new wall */
10216 MovDelay[ax][ay] = 6;
10218 if (MovDelay[ax][ay]) /* wait some time before building new wall */
10220 MovDelay[ax][ay]--;
10221 if (MovDelay[ax][ay])
10225 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10227 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10229 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10231 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10232 rechts_frei = TRUE;
10234 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10235 element == EL_EXPANDABLE_STEELWALL_ANY)
10239 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10240 Store[ax][ay-1] = element;
10241 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10242 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10243 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10244 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10249 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10250 Store[ax][ay+1] = element;
10251 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10252 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10253 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10254 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10259 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10260 element == EL_EXPANDABLE_STEELWALL_ANY)
10264 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10265 Store[ax-1][ay] = element;
10266 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10267 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10268 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10269 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10275 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10276 Store[ax+1][ay] = element;
10277 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10278 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10279 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10280 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10285 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10286 oben_massiv = TRUE;
10287 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10288 unten_massiv = TRUE;
10289 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10290 links_massiv = TRUE;
10291 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10292 rechts_massiv = TRUE;
10294 if (((oben_massiv && unten_massiv) ||
10295 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10296 ((links_massiv && rechts_massiv) ||
10297 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10298 Feld[ax][ay] = EL_STEELWALL;
10301 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10304 void CheckForDragon(int x, int y)
10307 boolean dragon_found = FALSE;
10308 static int xy[4][2] =
10316 for (i = 0; i < NUM_DIRECTIONS; i++)
10318 for (j = 0; j < 4; j++)
10320 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10322 if (IN_LEV_FIELD(xx, yy) &&
10323 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10325 if (Feld[xx][yy] == EL_DRAGON)
10326 dragon_found = TRUE;
10335 for (i = 0; i < NUM_DIRECTIONS; i++)
10337 for (j = 0; j < 3; j++)
10339 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10341 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10343 Feld[xx][yy] = EL_EMPTY;
10344 TEST_DrawLevelField(xx, yy);
10353 static void InitBuggyBase(int x, int y)
10355 int element = Feld[x][y];
10356 int activating_delay = FRAMES_PER_SECOND / 4;
10358 ChangeDelay[x][y] =
10359 (element == EL_SP_BUGGY_BASE ?
10360 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10361 element == EL_SP_BUGGY_BASE_ACTIVATING ?
10363 element == EL_SP_BUGGY_BASE_ACTIVE ?
10364 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10367 static void WarnBuggyBase(int x, int y)
10370 static int xy[4][2] =
10378 for (i = 0; i < NUM_DIRECTIONS; i++)
10380 int xx = x + xy[i][0];
10381 int yy = y + xy[i][1];
10383 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10385 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10392 static void InitTrap(int x, int y)
10394 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10397 static void ActivateTrap(int x, int y)
10399 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10402 static void ChangeActiveTrap(int x, int y)
10404 int graphic = IMG_TRAP_ACTIVE;
10406 /* if new animation frame was drawn, correct crumbled sand border */
10407 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10408 TEST_DrawLevelFieldCrumbled(x, y);
10411 static int getSpecialActionElement(int element, int number, int base_element)
10413 return (element != EL_EMPTY ? element :
10414 number != -1 ? base_element + number - 1 :
10418 static int getModifiedActionNumber(int value_old, int operator, int operand,
10419 int value_min, int value_max)
10421 int value_new = (operator == CA_MODE_SET ? operand :
10422 operator == CA_MODE_ADD ? value_old + operand :
10423 operator == CA_MODE_SUBTRACT ? value_old - operand :
10424 operator == CA_MODE_MULTIPLY ? value_old * operand :
10425 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
10426 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
10429 return (value_new < value_min ? value_min :
10430 value_new > value_max ? value_max :
10434 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10436 struct ElementInfo *ei = &element_info[element];
10437 struct ElementChangeInfo *change = &ei->change_page[page];
10438 int target_element = change->target_element;
10439 int action_type = change->action_type;
10440 int action_mode = change->action_mode;
10441 int action_arg = change->action_arg;
10442 int action_element = change->action_element;
10445 if (!change->has_action)
10448 /* ---------- determine action paramater values -------------------------- */
10450 int level_time_value =
10451 (level.time > 0 ? TimeLeft :
10454 int action_arg_element_raw =
10455 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
10456 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10457 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
10458 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
10459 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10460 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
10461 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
10463 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10466 if (action_arg_element_raw == EL_GROUP_START)
10467 printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10470 int action_arg_direction =
10471 (action_arg >= CA_ARG_DIRECTION_LEFT &&
10472 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10473 action_arg == CA_ARG_DIRECTION_TRIGGER ?
10474 change->actual_trigger_side :
10475 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10476 MV_DIR_OPPOSITE(change->actual_trigger_side) :
10479 int action_arg_number_min =
10480 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10483 int action_arg_number_max =
10484 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10485 action_type == CA_SET_LEVEL_GEMS ? 999 :
10486 action_type == CA_SET_LEVEL_TIME ? 9999 :
10487 action_type == CA_SET_LEVEL_SCORE ? 99999 :
10488 action_type == CA_SET_CE_VALUE ? 9999 :
10489 action_type == CA_SET_CE_SCORE ? 9999 :
10492 int action_arg_number_reset =
10493 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10494 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10495 action_type == CA_SET_LEVEL_TIME ? level.time :
10496 action_type == CA_SET_LEVEL_SCORE ? 0 :
10497 #if USE_NEW_CUSTOM_VALUE
10498 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10500 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10502 action_type == CA_SET_CE_SCORE ? 0 :
10505 int action_arg_number =
10506 (action_arg <= CA_ARG_MAX ? action_arg :
10507 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10508 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10509 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10510 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10511 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10512 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10513 #if USE_NEW_CUSTOM_VALUE
10514 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10516 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10518 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10519 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10520 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10521 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10522 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10523 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10524 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10525 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10526 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10527 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10528 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10529 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
10530 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10531 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
10534 int action_arg_number_old =
10535 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10536 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10537 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10538 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10539 action_type == CA_SET_CE_SCORE ? ei->collect_score :
10542 int action_arg_number_new =
10543 getModifiedActionNumber(action_arg_number_old,
10544 action_mode, action_arg_number,
10545 action_arg_number_min, action_arg_number_max);
10548 int trigger_player_bits =
10549 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10550 change->actual_trigger_player_bits : change->trigger_player);
10552 int trigger_player_bits =
10553 (change->actual_trigger_player >= EL_PLAYER_1 &&
10554 change->actual_trigger_player <= EL_PLAYER_4 ?
10555 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10559 int action_arg_player_bits =
10560 (action_arg >= CA_ARG_PLAYER_1 &&
10561 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10562 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10563 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10566 /* ---------- execute action -------------------------------------------- */
10568 switch (action_type)
10575 /* ---------- level actions ------------------------------------------- */
10577 case CA_RESTART_LEVEL:
10579 game.restart_level = TRUE;
10584 case CA_SHOW_ENVELOPE:
10586 int element = getSpecialActionElement(action_arg_element,
10587 action_arg_number, EL_ENVELOPE_1);
10589 if (IS_ENVELOPE(element))
10590 local_player->show_envelope = element;
10595 case CA_SET_LEVEL_TIME:
10597 if (level.time > 0) /* only modify limited time value */
10599 TimeLeft = action_arg_number_new;
10602 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10604 DisplayGameControlValues();
10606 DrawGameValue_Time(TimeLeft);
10609 if (!TimeLeft && setup.time_limit)
10610 for (i = 0; i < MAX_PLAYERS; i++)
10611 KillPlayer(&stored_player[i]);
10617 case CA_SET_LEVEL_SCORE:
10619 local_player->score = action_arg_number_new;
10622 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10624 DisplayGameControlValues();
10626 DrawGameValue_Score(local_player->score);
10632 case CA_SET_LEVEL_GEMS:
10634 local_player->gems_still_needed = action_arg_number_new;
10637 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10639 DisplayGameControlValues();
10641 DrawGameValue_Emeralds(local_player->gems_still_needed);
10647 #if !USE_PLAYER_GRAVITY
10648 case CA_SET_LEVEL_GRAVITY:
10650 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10651 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10652 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10658 case CA_SET_LEVEL_WIND:
10660 game.wind_direction = action_arg_direction;
10665 case CA_SET_LEVEL_RANDOM_SEED:
10668 /* ensure that setting a new random seed while playing is predictable */
10669 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10671 InitRND(action_arg_number_new);
10675 printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10683 for (i = 0; i < 9; i++)
10684 printf("%d, ", RND(2));
10692 /* ---------- player actions ------------------------------------------ */
10694 case CA_MOVE_PLAYER:
10696 /* automatically move to the next field in specified direction */
10697 for (i = 0; i < MAX_PLAYERS; i++)
10698 if (trigger_player_bits & (1 << i))
10699 stored_player[i].programmed_action = action_arg_direction;
10704 case CA_EXIT_PLAYER:
10706 for (i = 0; i < MAX_PLAYERS; i++)
10707 if (action_arg_player_bits & (1 << i))
10708 PlayerWins(&stored_player[i]);
10713 case CA_KILL_PLAYER:
10715 for (i = 0; i < MAX_PLAYERS; i++)
10716 if (action_arg_player_bits & (1 << i))
10717 KillPlayer(&stored_player[i]);
10722 case CA_SET_PLAYER_KEYS:
10724 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10725 int element = getSpecialActionElement(action_arg_element,
10726 action_arg_number, EL_KEY_1);
10728 if (IS_KEY(element))
10730 for (i = 0; i < MAX_PLAYERS; i++)
10732 if (trigger_player_bits & (1 << i))
10734 stored_player[i].key[KEY_NR(element)] = key_state;
10736 DrawGameDoorValues();
10744 case CA_SET_PLAYER_SPEED:
10747 printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10750 for (i = 0; i < MAX_PLAYERS; i++)
10752 if (trigger_player_bits & (1 << i))
10754 int move_stepsize = TILEX / stored_player[i].move_delay_value;
10756 if (action_arg == CA_ARG_SPEED_FASTER &&
10757 stored_player[i].cannot_move)
10759 action_arg_number = STEPSIZE_VERY_SLOW;
10761 else if (action_arg == CA_ARG_SPEED_SLOWER ||
10762 action_arg == CA_ARG_SPEED_FASTER)
10764 action_arg_number = 2;
10765 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10768 else if (action_arg == CA_ARG_NUMBER_RESET)
10770 action_arg_number = level.initial_player_stepsize[i];
10774 getModifiedActionNumber(move_stepsize,
10777 action_arg_number_min,
10778 action_arg_number_max);
10780 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10787 case CA_SET_PLAYER_SHIELD:
10789 for (i = 0; i < MAX_PLAYERS; i++)
10791 if (trigger_player_bits & (1 << i))
10793 if (action_arg == CA_ARG_SHIELD_OFF)
10795 stored_player[i].shield_normal_time_left = 0;
10796 stored_player[i].shield_deadly_time_left = 0;
10798 else if (action_arg == CA_ARG_SHIELD_NORMAL)
10800 stored_player[i].shield_normal_time_left = 999999;
10802 else if (action_arg == CA_ARG_SHIELD_DEADLY)
10804 stored_player[i].shield_normal_time_left = 999999;
10805 stored_player[i].shield_deadly_time_left = 999999;
10813 #if USE_PLAYER_GRAVITY
10814 case CA_SET_PLAYER_GRAVITY:
10816 for (i = 0; i < MAX_PLAYERS; i++)
10818 if (trigger_player_bits & (1 << i))
10820 stored_player[i].gravity =
10821 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10822 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10823 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10824 stored_player[i].gravity);
10832 case CA_SET_PLAYER_ARTWORK:
10834 for (i = 0; i < MAX_PLAYERS; i++)
10836 if (trigger_player_bits & (1 << i))
10838 int artwork_element = action_arg_element;
10840 if (action_arg == CA_ARG_ELEMENT_RESET)
10842 (level.use_artwork_element[i] ? level.artwork_element[i] :
10843 stored_player[i].element_nr);
10845 #if USE_GFX_RESET_PLAYER_ARTWORK
10846 if (stored_player[i].artwork_element != artwork_element)
10847 stored_player[i].Frame = 0;
10850 stored_player[i].artwork_element = artwork_element;
10852 SetPlayerWaiting(&stored_player[i], FALSE);
10854 /* set number of special actions for bored and sleeping animation */
10855 stored_player[i].num_special_action_bored =
10856 get_num_special_action(artwork_element,
10857 ACTION_BORING_1, ACTION_BORING_LAST);
10858 stored_player[i].num_special_action_sleeping =
10859 get_num_special_action(artwork_element,
10860 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10867 case CA_SET_PLAYER_INVENTORY:
10869 for (i = 0; i < MAX_PLAYERS; i++)
10871 struct PlayerInfo *player = &stored_player[i];
10874 if (trigger_player_bits & (1 << i))
10876 int inventory_element = action_arg_element;
10878 if (action_arg == CA_ARG_ELEMENT_TARGET ||
10879 action_arg == CA_ARG_ELEMENT_TRIGGER ||
10880 action_arg == CA_ARG_ELEMENT_ACTION)
10882 int element = inventory_element;
10883 int collect_count = element_info[element].collect_count_initial;
10885 if (!IS_CUSTOM_ELEMENT(element))
10888 if (collect_count == 0)
10889 player->inventory_infinite_element = element;
10891 for (k = 0; k < collect_count; k++)
10892 if (player->inventory_size < MAX_INVENTORY_SIZE)
10893 player->inventory_element[player->inventory_size++] =
10896 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10897 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10898 action_arg == CA_ARG_INVENTORY_RM_ACTION)
10900 if (player->inventory_infinite_element != EL_UNDEFINED &&
10901 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10902 action_arg_element_raw))
10903 player->inventory_infinite_element = EL_UNDEFINED;
10905 for (k = 0, j = 0; j < player->inventory_size; j++)
10907 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10908 action_arg_element_raw))
10909 player->inventory_element[k++] = player->inventory_element[j];
10912 player->inventory_size = k;
10914 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10916 if (player->inventory_size > 0)
10918 for (j = 0; j < player->inventory_size - 1; j++)
10919 player->inventory_element[j] = player->inventory_element[j + 1];
10921 player->inventory_size--;
10924 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10926 if (player->inventory_size > 0)
10927 player->inventory_size--;
10929 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10931 player->inventory_infinite_element = EL_UNDEFINED;
10932 player->inventory_size = 0;
10934 else if (action_arg == CA_ARG_INVENTORY_RESET)
10936 player->inventory_infinite_element = EL_UNDEFINED;
10937 player->inventory_size = 0;
10939 if (level.use_initial_inventory[i])
10941 for (j = 0; j < level.initial_inventory_size[i]; j++)
10943 int element = level.initial_inventory_content[i][j];
10944 int collect_count = element_info[element].collect_count_initial;
10946 if (!IS_CUSTOM_ELEMENT(element))
10949 if (collect_count == 0)
10950 player->inventory_infinite_element = element;
10952 for (k = 0; k < collect_count; k++)
10953 if (player->inventory_size < MAX_INVENTORY_SIZE)
10954 player->inventory_element[player->inventory_size++] =
10965 /* ---------- CE actions ---------------------------------------------- */
10967 case CA_SET_CE_VALUE:
10969 #if USE_NEW_CUSTOM_VALUE
10970 int last_ce_value = CustomValue[x][y];
10972 CustomValue[x][y] = action_arg_number_new;
10974 if (CustomValue[x][y] != last_ce_value)
10976 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10977 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10979 if (CustomValue[x][y] == 0)
10981 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10982 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10990 case CA_SET_CE_SCORE:
10992 #if USE_NEW_CUSTOM_VALUE
10993 int last_ce_score = ei->collect_score;
10995 ei->collect_score = action_arg_number_new;
10997 if (ei->collect_score != last_ce_score)
10999 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
11000 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
11002 if (ei->collect_score == 0)
11006 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
11007 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
11010 This is a very special case that seems to be a mixture between
11011 CheckElementChange() and CheckTriggeredElementChange(): while
11012 the first one only affects single elements that are triggered
11013 directly, the second one affects multiple elements in the playfield
11014 that are triggered indirectly by another element. This is a third
11015 case: Changing the CE score always affects multiple identical CEs,
11016 so every affected CE must be checked, not only the single CE for
11017 which the CE score was changed in the first place (as every instance
11018 of that CE shares the same CE score, and therefore also can change)!
11020 SCAN_PLAYFIELD(xx, yy)
11022 if (Feld[xx][yy] == element)
11023 CheckElementChange(xx, yy, element, EL_UNDEFINED,
11024 CE_SCORE_GETS_ZERO);
11033 case CA_SET_CE_ARTWORK:
11035 int artwork_element = action_arg_element;
11036 boolean reset_frame = FALSE;
11039 if (action_arg == CA_ARG_ELEMENT_RESET)
11040 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
11043 if (ei->gfx_element != artwork_element)
11044 reset_frame = TRUE;
11046 ei->gfx_element = artwork_element;
11048 SCAN_PLAYFIELD(xx, yy)
11050 if (Feld[xx][yy] == element)
11054 ResetGfxAnimation(xx, yy);
11055 ResetRandomAnimationValue(xx, yy);
11058 TEST_DrawLevelField(xx, yy);
11065 /* ---------- engine actions ------------------------------------------ */
11067 case CA_SET_ENGINE_SCAN_MODE:
11069 InitPlayfieldScanMode(action_arg);
11079 static void CreateFieldExt(int x, int y, int element, boolean is_change)
11081 int old_element = Feld[x][y];
11082 int new_element = GetElementFromGroupElement(element);
11083 int previous_move_direction = MovDir[x][y];
11084 #if USE_NEW_CUSTOM_VALUE
11085 int last_ce_value = CustomValue[x][y];
11087 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
11088 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
11089 boolean add_player_onto_element = (new_element_is_player &&
11090 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
11091 /* this breaks SnakeBite when a snake is
11092 halfway through a door that closes */
11093 /* NOW FIXED AT LEVEL INIT IN files.c */
11094 new_element != EL_SOKOBAN_FIELD_PLAYER &&
11096 IS_WALKABLE(old_element));
11099 /* check if element under the player changes from accessible to unaccessible
11100 (needed for special case of dropping element which then changes) */
11101 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11102 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11110 if (!add_player_onto_element)
11112 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
11113 RemoveMovingField(x, y);
11117 Feld[x][y] = new_element;
11119 #if !USE_GFX_RESET_GFX_ANIMATION
11120 ResetGfxAnimation(x, y);
11121 ResetRandomAnimationValue(x, y);
11124 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
11125 MovDir[x][y] = previous_move_direction;
11127 #if USE_NEW_CUSTOM_VALUE
11128 if (element_info[new_element].use_last_ce_value)
11129 CustomValue[x][y] = last_ce_value;
11132 InitField_WithBug1(x, y, FALSE);
11134 new_element = Feld[x][y]; /* element may have changed */
11136 #if USE_GFX_RESET_GFX_ANIMATION
11137 ResetGfxAnimation(x, y);
11138 ResetRandomAnimationValue(x, y);
11141 TEST_DrawLevelField(x, y);
11143 if (GFX_CRUMBLED(new_element))
11144 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
11148 /* check if element under the player changes from accessible to unaccessible
11149 (needed for special case of dropping element which then changes) */
11150 /* (must be checked after creating new element for walkable group elements) */
11151 #if USE_FIX_KILLED_BY_NON_WALKABLE
11152 if (IS_PLAYER(x, y) && !player_explosion_protected &&
11153 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11160 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11161 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11170 /* "ChangeCount" not set yet to allow "entered by player" change one time */
11171 if (new_element_is_player)
11172 RelocatePlayer(x, y, new_element);
11175 ChangeCount[x][y]++; /* count number of changes in the same frame */
11177 TestIfBadThingTouchesPlayer(x, y);
11178 TestIfPlayerTouchesCustomElement(x, y);
11179 TestIfElementTouchesCustomElement(x, y);
11182 static void CreateField(int x, int y, int element)
11184 CreateFieldExt(x, y, element, FALSE);
11187 static void CreateElementFromChange(int x, int y, int element)
11189 element = GET_VALID_RUNTIME_ELEMENT(element);
11191 #if USE_STOP_CHANGED_ELEMENTS
11192 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11194 int old_element = Feld[x][y];
11196 /* prevent changed element from moving in same engine frame
11197 unless both old and new element can either fall or move */
11198 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
11199 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
11204 CreateFieldExt(x, y, element, TRUE);
11207 static boolean ChangeElement(int x, int y, int element, int page)
11209 struct ElementInfo *ei = &element_info[element];
11210 struct ElementChangeInfo *change = &ei->change_page[page];
11211 int ce_value = CustomValue[x][y];
11212 int ce_score = ei->collect_score;
11213 int target_element;
11214 int old_element = Feld[x][y];
11216 /* always use default change event to prevent running into a loop */
11217 if (ChangeEvent[x][y] == -1)
11218 ChangeEvent[x][y] = CE_DELAY;
11220 if (ChangeEvent[x][y] == CE_DELAY)
11222 /* reset actual trigger element, trigger player and action element */
11223 change->actual_trigger_element = EL_EMPTY;
11224 change->actual_trigger_player = EL_EMPTY;
11225 change->actual_trigger_player_bits = CH_PLAYER_NONE;
11226 change->actual_trigger_side = CH_SIDE_NONE;
11227 change->actual_trigger_ce_value = 0;
11228 change->actual_trigger_ce_score = 0;
11231 /* do not change elements more than a specified maximum number of changes */
11232 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11235 ChangeCount[x][y]++; /* count number of changes in the same frame */
11237 if (change->explode)
11244 if (change->use_target_content)
11246 boolean complete_replace = TRUE;
11247 boolean can_replace[3][3];
11250 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11253 boolean is_walkable;
11254 boolean is_diggable;
11255 boolean is_collectible;
11256 boolean is_removable;
11257 boolean is_destructible;
11258 int ex = x + xx - 1;
11259 int ey = y + yy - 1;
11260 int content_element = change->target_content.e[xx][yy];
11263 can_replace[xx][yy] = TRUE;
11265 if (ex == x && ey == y) /* do not check changing element itself */
11268 if (content_element == EL_EMPTY_SPACE)
11270 can_replace[xx][yy] = FALSE; /* do not replace border with space */
11275 if (!IN_LEV_FIELD(ex, ey))
11277 can_replace[xx][yy] = FALSE;
11278 complete_replace = FALSE;
11285 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11286 e = MovingOrBlocked2Element(ex, ey);
11288 is_empty = (IS_FREE(ex, ey) ||
11289 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11291 is_walkable = (is_empty || IS_WALKABLE(e));
11292 is_diggable = (is_empty || IS_DIGGABLE(e));
11293 is_collectible = (is_empty || IS_COLLECTIBLE(e));
11294 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11295 is_removable = (is_diggable || is_collectible);
11297 can_replace[xx][yy] =
11298 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
11299 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
11300 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
11301 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
11302 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
11303 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11304 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11306 if (!can_replace[xx][yy])
11307 complete_replace = FALSE;
11310 if (!change->only_if_complete || complete_replace)
11312 boolean something_has_changed = FALSE;
11314 if (change->only_if_complete && change->use_random_replace &&
11315 RND(100) < change->random_percentage)
11318 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11320 int ex = x + xx - 1;
11321 int ey = y + yy - 1;
11322 int content_element;
11324 if (can_replace[xx][yy] && (!change->use_random_replace ||
11325 RND(100) < change->random_percentage))
11327 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11328 RemoveMovingField(ex, ey);
11330 ChangeEvent[ex][ey] = ChangeEvent[x][y];
11332 content_element = change->target_content.e[xx][yy];
11333 target_element = GET_TARGET_ELEMENT(element, content_element, change,
11334 ce_value, ce_score);
11336 CreateElementFromChange(ex, ey, target_element);
11338 something_has_changed = TRUE;
11340 /* for symmetry reasons, freeze newly created border elements */
11341 if (ex != x || ey != y)
11342 Stop[ex][ey] = TRUE; /* no more moving in this frame */
11346 if (something_has_changed)
11348 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11349 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11355 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11356 ce_value, ce_score);
11358 if (element == EL_DIAGONAL_GROWING ||
11359 element == EL_DIAGONAL_SHRINKING)
11361 target_element = Store[x][y];
11363 Store[x][y] = EL_EMPTY;
11366 CreateElementFromChange(x, y, target_element);
11368 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11369 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11372 /* this uses direct change before indirect change */
11373 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11378 #if USE_NEW_DELAYED_ACTION
11380 static void HandleElementChange(int x, int y, int page)
11382 int element = MovingOrBlocked2Element(x, y);
11383 struct ElementInfo *ei = &element_info[element];
11384 struct ElementChangeInfo *change = &ei->change_page[page];
11385 boolean handle_action_before_change = FALSE;
11388 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11389 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11392 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11393 x, y, element, element_info[element].token_name);
11394 printf("HandleElementChange(): This should never happen!\n");
11399 /* this can happen with classic bombs on walkable, changing elements */
11400 if (!CAN_CHANGE_OR_HAS_ACTION(element))
11403 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11404 ChangeDelay[x][y] = 0;
11410 if (ChangeDelay[x][y] == 0) /* initialize element change */
11412 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11414 if (change->can_change)
11417 /* !!! not clear why graphic animation should be reset at all here !!! */
11418 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11419 #if USE_GFX_RESET_WHEN_NOT_MOVING
11420 /* when a custom element is about to change (for example by change delay),
11421 do not reset graphic animation when the custom element is moving */
11422 if (!IS_MOVING(x, y))
11425 ResetGfxAnimation(x, y);
11426 ResetRandomAnimationValue(x, y);
11430 if (change->pre_change_function)
11431 change->pre_change_function(x, y);
11435 ChangeDelay[x][y]--;
11437 if (ChangeDelay[x][y] != 0) /* continue element change */
11439 if (change->can_change)
11441 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11443 if (IS_ANIMATED(graphic))
11444 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11446 if (change->change_function)
11447 change->change_function(x, y);
11450 else /* finish element change */
11452 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11454 page = ChangePage[x][y];
11455 ChangePage[x][y] = -1;
11457 change = &ei->change_page[page];
11460 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11462 ChangeDelay[x][y] = 1; /* try change after next move step */
11463 ChangePage[x][y] = page; /* remember page to use for change */
11469 /* special case: set new level random seed before changing element */
11470 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11471 handle_action_before_change = TRUE;
11473 if (change->has_action && handle_action_before_change)
11474 ExecuteCustomElementAction(x, y, element, page);
11477 if (change->can_change)
11479 if (ChangeElement(x, y, element, page))
11481 if (change->post_change_function)
11482 change->post_change_function(x, y);
11486 if (change->has_action && !handle_action_before_change)
11487 ExecuteCustomElementAction(x, y, element, page);
11493 static void HandleElementChange(int x, int y, int page)
11495 int element = MovingOrBlocked2Element(x, y);
11496 struct ElementInfo *ei = &element_info[element];
11497 struct ElementChangeInfo *change = &ei->change_page[page];
11500 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11503 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11504 x, y, element, element_info[element].token_name);
11505 printf("HandleElementChange(): This should never happen!\n");
11510 /* this can happen with classic bombs on walkable, changing elements */
11511 if (!CAN_CHANGE(element))
11514 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11515 ChangeDelay[x][y] = 0;
11521 if (ChangeDelay[x][y] == 0) /* initialize element change */
11523 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11525 ResetGfxAnimation(x, y);
11526 ResetRandomAnimationValue(x, y);
11528 if (change->pre_change_function)
11529 change->pre_change_function(x, y);
11532 ChangeDelay[x][y]--;
11534 if (ChangeDelay[x][y] != 0) /* continue element change */
11536 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11538 if (IS_ANIMATED(graphic))
11539 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11541 if (change->change_function)
11542 change->change_function(x, y);
11544 else /* finish element change */
11546 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11548 page = ChangePage[x][y];
11549 ChangePage[x][y] = -1;
11551 change = &ei->change_page[page];
11554 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11556 ChangeDelay[x][y] = 1; /* try change after next move step */
11557 ChangePage[x][y] = page; /* remember page to use for change */
11562 if (ChangeElement(x, y, element, page))
11564 if (change->post_change_function)
11565 change->post_change_function(x, y);
11572 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11573 int trigger_element,
11575 int trigger_player,
11579 boolean change_done_any = FALSE;
11580 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11583 if (!(trigger_events[trigger_element][trigger_event]))
11587 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11588 trigger_event, recursion_loop_depth, recursion_loop_detected,
11589 recursion_loop_element, EL_NAME(recursion_loop_element));
11592 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11594 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11596 int element = EL_CUSTOM_START + i;
11597 boolean change_done = FALSE;
11600 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11601 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11604 for (p = 0; p < element_info[element].num_change_pages; p++)
11606 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11608 if (change->can_change_or_has_action &&
11609 change->has_event[trigger_event] &&
11610 change->trigger_side & trigger_side &&
11611 change->trigger_player & trigger_player &&
11612 change->trigger_page & trigger_page_bits &&
11613 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11615 change->actual_trigger_element = trigger_element;
11616 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11617 change->actual_trigger_player_bits = trigger_player;
11618 change->actual_trigger_side = trigger_side;
11619 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11620 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11623 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11624 element, EL_NAME(element), p);
11627 if ((change->can_change && !change_done) || change->has_action)
11631 SCAN_PLAYFIELD(x, y)
11633 if (Feld[x][y] == element)
11635 if (change->can_change && !change_done)
11637 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11638 /* if element already changed in this frame, not only prevent
11639 another element change (checked in ChangeElement()), but
11640 also prevent additional element actions for this element */
11642 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11643 !level.use_action_after_change_bug)
11648 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11649 element, EL_NAME(element), p);
11652 ChangeDelay[x][y] = 1;
11653 ChangeEvent[x][y] = trigger_event;
11655 HandleElementChange(x, y, p);
11657 #if USE_NEW_DELAYED_ACTION
11658 else if (change->has_action)
11660 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11661 /* if element already changed in this frame, not only prevent
11662 another element change (checked in ChangeElement()), but
11663 also prevent additional element actions for this element */
11665 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11666 !level.use_action_after_change_bug)
11672 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11673 element, EL_NAME(element), p);
11676 ExecuteCustomElementAction(x, y, element, p);
11677 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11680 if (change->has_action)
11682 ExecuteCustomElementAction(x, y, element, p);
11683 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11689 if (change->can_change)
11691 change_done = TRUE;
11692 change_done_any = TRUE;
11695 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11696 element, EL_NAME(element), p);
11705 RECURSION_LOOP_DETECTION_END();
11707 return change_done_any;
11710 static boolean CheckElementChangeExt(int x, int y,
11712 int trigger_element,
11714 int trigger_player,
11717 boolean change_done = FALSE;
11720 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11721 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11724 if (Feld[x][y] == EL_BLOCKED)
11726 Blocked2Moving(x, y, &x, &y);
11727 element = Feld[x][y];
11731 /* check if element has already changed */
11732 if (Feld[x][y] != element)
11735 /* check if element has already changed or is about to change after moving */
11736 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11737 Feld[x][y] != element) ||
11739 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11740 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11741 ChangePage[x][y] != -1)))
11746 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11747 trigger_event, recursion_loop_depth, recursion_loop_detected,
11748 recursion_loop_element, EL_NAME(recursion_loop_element));
11751 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11754 printf("::: X: trigger_player_bits == %d\n", trigger_player);
11757 for (p = 0; p < element_info[element].num_change_pages; p++)
11759 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11761 /* check trigger element for all events where the element that is checked
11762 for changing interacts with a directly adjacent element -- this is
11763 different to element changes that affect other elements to change on the
11764 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11765 boolean check_trigger_element =
11766 (trigger_event == CE_TOUCHING_X ||
11767 trigger_event == CE_HITTING_X ||
11768 trigger_event == CE_HIT_BY_X ||
11770 /* this one was forgotten until 3.2.3 */
11771 trigger_event == CE_DIGGING_X);
11774 if (change->can_change_or_has_action &&
11775 change->has_event[trigger_event] &&
11776 change->trigger_side & trigger_side &&
11777 change->trigger_player & trigger_player &&
11778 (!check_trigger_element ||
11779 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11781 change->actual_trigger_element = trigger_element;
11782 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11783 change->actual_trigger_player_bits = trigger_player;
11784 change->actual_trigger_side = trigger_side;
11785 change->actual_trigger_ce_value = CustomValue[x][y];
11786 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11788 /* special case: trigger element not at (x,y) position for some events */
11789 if (check_trigger_element)
11801 { 0, 0 }, { 0, 0 }, { 0, 0 },
11805 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11806 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11808 change->actual_trigger_ce_value = CustomValue[xx][yy];
11809 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11812 if (change->can_change && !change_done)
11814 ChangeDelay[x][y] = 1;
11815 ChangeEvent[x][y] = trigger_event;
11817 HandleElementChange(x, y, p);
11819 change_done = TRUE;
11821 #if USE_NEW_DELAYED_ACTION
11822 else if (change->has_action)
11824 ExecuteCustomElementAction(x, y, element, p);
11825 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11828 if (change->has_action)
11830 ExecuteCustomElementAction(x, y, element, p);
11831 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11837 RECURSION_LOOP_DETECTION_END();
11839 return change_done;
11842 static void PlayPlayerSound(struct PlayerInfo *player)
11844 int jx = player->jx, jy = player->jy;
11845 int sound_element = player->artwork_element;
11846 int last_action = player->last_action_waiting;
11847 int action = player->action_waiting;
11849 if (player->is_waiting)
11851 if (action != last_action)
11852 PlayLevelSoundElementAction(jx, jy, sound_element, action);
11854 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11858 if (action != last_action)
11859 StopSound(element_info[sound_element].sound[last_action]);
11861 if (last_action == ACTION_SLEEPING)
11862 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11866 static void PlayAllPlayersSound()
11870 for (i = 0; i < MAX_PLAYERS; i++)
11871 if (stored_player[i].active)
11872 PlayPlayerSound(&stored_player[i]);
11875 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11877 boolean last_waiting = player->is_waiting;
11878 int move_dir = player->MovDir;
11880 player->dir_waiting = move_dir;
11881 player->last_action_waiting = player->action_waiting;
11885 if (!last_waiting) /* not waiting -> waiting */
11887 player->is_waiting = TRUE;
11889 player->frame_counter_bored =
11891 game.player_boring_delay_fixed +
11892 GetSimpleRandom(game.player_boring_delay_random);
11893 player->frame_counter_sleeping =
11895 game.player_sleeping_delay_fixed +
11896 GetSimpleRandom(game.player_sleeping_delay_random);
11898 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11901 if (game.player_sleeping_delay_fixed +
11902 game.player_sleeping_delay_random > 0 &&
11903 player->anim_delay_counter == 0 &&
11904 player->post_delay_counter == 0 &&
11905 FrameCounter >= player->frame_counter_sleeping)
11906 player->is_sleeping = TRUE;
11907 else if (game.player_boring_delay_fixed +
11908 game.player_boring_delay_random > 0 &&
11909 FrameCounter >= player->frame_counter_bored)
11910 player->is_bored = TRUE;
11912 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11913 player->is_bored ? ACTION_BORING :
11916 if (player->is_sleeping && player->use_murphy)
11918 /* special case for sleeping Murphy when leaning against non-free tile */
11920 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11921 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11922 !IS_MOVING(player->jx - 1, player->jy)))
11923 move_dir = MV_LEFT;
11924 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11925 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11926 !IS_MOVING(player->jx + 1, player->jy)))
11927 move_dir = MV_RIGHT;
11929 player->is_sleeping = FALSE;
11931 player->dir_waiting = move_dir;
11934 if (player->is_sleeping)
11936 if (player->num_special_action_sleeping > 0)
11938 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11940 int last_special_action = player->special_action_sleeping;
11941 int num_special_action = player->num_special_action_sleeping;
11942 int special_action =
11943 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11944 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11945 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11946 last_special_action + 1 : ACTION_SLEEPING);
11947 int special_graphic =
11948 el_act_dir2img(player->artwork_element, special_action, move_dir);
11950 player->anim_delay_counter =
11951 graphic_info[special_graphic].anim_delay_fixed +
11952 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11953 player->post_delay_counter =
11954 graphic_info[special_graphic].post_delay_fixed +
11955 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11957 player->special_action_sleeping = special_action;
11960 if (player->anim_delay_counter > 0)
11962 player->action_waiting = player->special_action_sleeping;
11963 player->anim_delay_counter--;
11965 else if (player->post_delay_counter > 0)
11967 player->post_delay_counter--;
11971 else if (player->is_bored)
11973 if (player->num_special_action_bored > 0)
11975 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11977 int special_action =
11978 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11979 int special_graphic =
11980 el_act_dir2img(player->artwork_element, special_action, move_dir);
11982 player->anim_delay_counter =
11983 graphic_info[special_graphic].anim_delay_fixed +
11984 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11985 player->post_delay_counter =
11986 graphic_info[special_graphic].post_delay_fixed +
11987 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11989 player->special_action_bored = special_action;
11992 if (player->anim_delay_counter > 0)
11994 player->action_waiting = player->special_action_bored;
11995 player->anim_delay_counter--;
11997 else if (player->post_delay_counter > 0)
11999 player->post_delay_counter--;
12004 else if (last_waiting) /* waiting -> not waiting */
12006 player->is_waiting = FALSE;
12007 player->is_bored = FALSE;
12008 player->is_sleeping = FALSE;
12010 player->frame_counter_bored = -1;
12011 player->frame_counter_sleeping = -1;
12013 player->anim_delay_counter = 0;
12014 player->post_delay_counter = 0;
12016 player->dir_waiting = player->MovDir;
12017 player->action_waiting = ACTION_DEFAULT;
12019 player->special_action_bored = ACTION_DEFAULT;
12020 player->special_action_sleeping = ACTION_DEFAULT;
12024 static void CheckSingleStepMode(struct PlayerInfo *player)
12026 if (tape.single_step && tape.recording && !tape.pausing)
12028 /* as it is called "single step mode", just return to pause mode when the
12029 player stopped moving after one tile (or never starts moving at all) */
12030 if (!player->is_moving && !player->is_pushing)
12032 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12033 SnapField(player, 0, 0); /* stop snapping */
12038 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
12040 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
12041 int left = player_action & JOY_LEFT;
12042 int right = player_action & JOY_RIGHT;
12043 int up = player_action & JOY_UP;
12044 int down = player_action & JOY_DOWN;
12045 int button1 = player_action & JOY_BUTTON_1;
12046 int button2 = player_action & JOY_BUTTON_2;
12047 int dx = (left ? -1 : right ? 1 : 0);
12048 int dy = (up ? -1 : down ? 1 : 0);
12050 if (!player->active || tape.pausing)
12056 snapped = SnapField(player, dx, dy);
12060 dropped = DropElement(player);
12062 moved = MovePlayer(player, dx, dy);
12065 CheckSingleStepMode(player);
12067 SetPlayerWaiting(player, FALSE);
12069 return player_action;
12073 /* no actions for this player (no input at player's configured device) */
12075 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
12076 SnapField(player, 0, 0);
12077 CheckGravityMovementWhenNotMoving(player);
12079 if (player->MovPos == 0)
12080 SetPlayerWaiting(player, TRUE);
12082 if (player->MovPos == 0) /* needed for tape.playing */
12083 player->is_moving = FALSE;
12085 player->is_dropping = FALSE;
12086 player->is_dropping_pressed = FALSE;
12087 player->drop_pressed_delay = 0;
12089 CheckSingleStepMode(player);
12095 static void CheckLevelTime()
12099 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
12100 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12102 if (level.native_em_level->lev->home == 0) /* all players at home */
12104 PlayerWins(local_player);
12106 AllPlayersGone = TRUE;
12108 level.native_em_level->lev->home = -1;
12111 if (level.native_em_level->ply[0]->alive == 0 &&
12112 level.native_em_level->ply[1]->alive == 0 &&
12113 level.native_em_level->ply[2]->alive == 0 &&
12114 level.native_em_level->ply[3]->alive == 0) /* all dead */
12115 AllPlayersGone = TRUE;
12117 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12119 if (game_sp.LevelSolved &&
12120 !game_sp.GameOver) /* game won */
12122 PlayerWins(local_player);
12124 game_sp.GameOver = TRUE;
12126 AllPlayersGone = TRUE;
12129 if (game_sp.GameOver) /* game lost */
12130 AllPlayersGone = TRUE;
12133 if (TimeFrames >= FRAMES_PER_SECOND)
12138 for (i = 0; i < MAX_PLAYERS; i++)
12140 struct PlayerInfo *player = &stored_player[i];
12142 if (SHIELD_ON(player))
12144 player->shield_normal_time_left--;
12146 if (player->shield_deadly_time_left > 0)
12147 player->shield_deadly_time_left--;
12151 if (!local_player->LevelSolved && !level.use_step_counter)
12159 if (TimeLeft <= 10 && setup.time_limit)
12160 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12163 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12165 DisplayGameControlValues();
12167 DrawGameValue_Time(TimeLeft);
12170 if (!TimeLeft && setup.time_limit)
12172 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12173 level.native_em_level->lev->killed_out_of_time = TRUE;
12175 for (i = 0; i < MAX_PLAYERS; i++)
12176 KillPlayer(&stored_player[i]);
12180 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12182 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12184 DisplayGameControlValues();
12187 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12188 DrawGameValue_Time(TimePlayed);
12191 level.native_em_level->lev->time =
12192 (level.time == 0 ? TimePlayed : TimeLeft);
12195 if (tape.recording || tape.playing)
12196 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12200 UpdateAndDisplayGameControlValues();
12202 UpdateGameDoorValues();
12203 DrawGameDoorValues();
12207 void AdvanceFrameAndPlayerCounters(int player_nr)
12211 /* advance frame counters (global frame counter and time frame counter) */
12215 /* advance player counters (counters for move delay, move animation etc.) */
12216 for (i = 0; i < MAX_PLAYERS; i++)
12218 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12219 int move_delay_value = stored_player[i].move_delay_value;
12220 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12222 if (!advance_player_counters) /* not all players may be affected */
12225 #if USE_NEW_PLAYER_ANIM
12226 if (move_frames == 0) /* less than one move per game frame */
12228 int stepsize = TILEX / move_delay_value;
12229 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12230 int count = (stored_player[i].is_moving ?
12231 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12233 if (count % delay == 0)
12238 stored_player[i].Frame += move_frames;
12240 if (stored_player[i].MovPos != 0)
12241 stored_player[i].StepFrame += move_frames;
12243 if (stored_player[i].move_delay > 0)
12244 stored_player[i].move_delay--;
12246 /* due to bugs in previous versions, counter must count up, not down */
12247 if (stored_player[i].push_delay != -1)
12248 stored_player[i].push_delay++;
12250 if (stored_player[i].drop_delay > 0)
12251 stored_player[i].drop_delay--;
12253 if (stored_player[i].is_dropping_pressed)
12254 stored_player[i].drop_pressed_delay++;
12258 void StartGameActions(boolean init_network_game, boolean record_tape,
12261 unsigned long new_random_seed = InitRND(random_seed);
12264 TapeStartRecording(new_random_seed);
12266 #if defined(NETWORK_AVALIABLE)
12267 if (init_network_game)
12269 SendToServer_StartPlaying();
12280 static unsigned long game_frame_delay = 0;
12281 unsigned long game_frame_delay_value;
12282 byte *recorded_player_action;
12283 byte summarized_player_action = 0;
12284 byte tape_action[MAX_PLAYERS];
12287 /* detect endless loops, caused by custom element programming */
12288 if (recursion_loop_detected && recursion_loop_depth == 0)
12290 char *message = getStringCat3("Internal Error ! Element ",
12291 EL_NAME(recursion_loop_element),
12292 " caused endless loop ! Quit the game ?");
12294 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12295 EL_NAME(recursion_loop_element));
12297 RequestQuitGameExt(FALSE, level_editor_test_game, message);
12299 recursion_loop_detected = FALSE; /* if game should be continued */
12306 if (game.restart_level)
12307 StartGameActions(options.network, setup.autorecord, level.random_seed);
12309 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
12310 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12312 if (level.native_em_level->lev->home == 0) /* all players at home */
12314 PlayerWins(local_player);
12316 AllPlayersGone = TRUE;
12318 level.native_em_level->lev->home = -1;
12321 if (level.native_em_level->ply[0]->alive == 0 &&
12322 level.native_em_level->ply[1]->alive == 0 &&
12323 level.native_em_level->ply[2]->alive == 0 &&
12324 level.native_em_level->ply[3]->alive == 0) /* all dead */
12325 AllPlayersGone = TRUE;
12327 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12329 if (game_sp.LevelSolved &&
12330 !game_sp.GameOver) /* game won */
12332 PlayerWins(local_player);
12334 game_sp.GameOver = TRUE;
12336 AllPlayersGone = TRUE;
12339 if (game_sp.GameOver) /* game lost */
12340 AllPlayersGone = TRUE;
12343 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12346 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12349 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
12352 game_frame_delay_value =
12353 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12355 if (tape.playing && tape.warp_forward && !tape.pausing)
12356 game_frame_delay_value = 0;
12358 /* ---------- main game synchronization point ---------- */
12360 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12362 if (network_playing && !network_player_action_received)
12364 /* try to get network player actions in time */
12366 #if defined(NETWORK_AVALIABLE)
12367 /* last chance to get network player actions without main loop delay */
12368 HandleNetworking();
12371 /* game was quit by network peer */
12372 if (game_status != GAME_MODE_PLAYING)
12375 if (!network_player_action_received)
12376 return; /* failed to get network player actions in time */
12378 /* do not yet reset "network_player_action_received" (for tape.pausing) */
12384 /* at this point we know that we really continue executing the game */
12386 network_player_action_received = FALSE;
12388 /* when playing tape, read previously recorded player input from tape data */
12389 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12392 /* TapePlayAction() may return NULL when toggling to "pause before death" */
12397 if (tape.set_centered_player)
12399 game.centered_player_nr_next = tape.centered_player_nr_next;
12400 game.set_centered_player = TRUE;
12403 for (i = 0; i < MAX_PLAYERS; i++)
12405 summarized_player_action |= stored_player[i].action;
12407 if (!network_playing)
12408 stored_player[i].effective_action = stored_player[i].action;
12411 #if defined(NETWORK_AVALIABLE)
12412 if (network_playing)
12413 SendToServer_MovePlayer(summarized_player_action);
12416 if (!options.network && !setup.team_mode)
12417 local_player->effective_action = summarized_player_action;
12419 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
12421 for (i = 0; i < MAX_PLAYERS; i++)
12422 stored_player[i].effective_action =
12423 (i == game.centered_player_nr ? summarized_player_action : 0);
12426 if (recorded_player_action != NULL)
12427 for (i = 0; i < MAX_PLAYERS; i++)
12428 stored_player[i].effective_action = recorded_player_action[i];
12430 for (i = 0; i < MAX_PLAYERS; i++)
12432 tape_action[i] = stored_player[i].effective_action;
12434 /* (this can only happen in the R'n'D game engine) */
12435 if (tape.recording && tape_action[i] && !tape.player_participates[i])
12436 tape.player_participates[i] = TRUE; /* player just appeared from CE */
12439 /* only record actions from input devices, but not programmed actions */
12440 if (tape.recording)
12441 TapeRecordAction(tape_action);
12443 #if USE_NEW_PLAYER_ASSIGNMENTS
12445 byte mapped_action[MAX_PLAYERS];
12447 for (i = 0; i < MAX_PLAYERS; i++)
12448 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12450 for (i = 0; i < MAX_PLAYERS; i++)
12451 stored_player[i].effective_action = mapped_action[i];
12455 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12457 GameActions_EM_Main();
12459 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12461 GameActions_SP_Main();
12469 void GameActions_EM_Main()
12471 byte effective_action[MAX_PLAYERS];
12472 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12475 for (i = 0; i < MAX_PLAYERS; i++)
12476 effective_action[i] = stored_player[i].effective_action;
12478 GameActions_EM(effective_action, warp_mode);
12482 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12485 void GameActions_SP_Main()
12487 byte effective_action[MAX_PLAYERS];
12488 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12491 for (i = 0; i < MAX_PLAYERS; i++)
12492 effective_action[i] = stored_player[i].effective_action;
12494 GameActions_SP(effective_action, warp_mode);
12498 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12501 void GameActions_RND()
12503 int magic_wall_x = 0, magic_wall_y = 0;
12504 int i, x, y, element, graphic;
12506 InitPlayfieldScanModeVars();
12508 #if USE_ONE_MORE_CHANGE_PER_FRAME
12509 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12511 SCAN_PLAYFIELD(x, y)
12513 ChangeCount[x][y] = 0;
12514 ChangeEvent[x][y] = -1;
12519 if (game.set_centered_player)
12521 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12523 /* switching to "all players" only possible if all players fit to screen */
12524 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12526 game.centered_player_nr_next = game.centered_player_nr;
12527 game.set_centered_player = FALSE;
12530 /* do not switch focus to non-existing (or non-active) player */
12531 if (game.centered_player_nr_next >= 0 &&
12532 !stored_player[game.centered_player_nr_next].active)
12534 game.centered_player_nr_next = game.centered_player_nr;
12535 game.set_centered_player = FALSE;
12539 if (game.set_centered_player &&
12540 ScreenMovPos == 0) /* screen currently aligned at tile position */
12544 if (game.centered_player_nr_next == -1)
12546 setScreenCenteredToAllPlayers(&sx, &sy);
12550 sx = stored_player[game.centered_player_nr_next].jx;
12551 sy = stored_player[game.centered_player_nr_next].jy;
12554 game.centered_player_nr = game.centered_player_nr_next;
12555 game.set_centered_player = FALSE;
12557 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12558 DrawGameDoorValues();
12561 for (i = 0; i < MAX_PLAYERS; i++)
12563 int actual_player_action = stored_player[i].effective_action;
12566 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12567 - rnd_equinox_tetrachloride 048
12568 - rnd_equinox_tetrachloride_ii 096
12569 - rnd_emanuel_schmieg 002
12570 - doctor_sloan_ww 001, 020
12572 if (stored_player[i].MovPos == 0)
12573 CheckGravityMovement(&stored_player[i]);
12576 /* overwrite programmed action with tape action */
12577 if (stored_player[i].programmed_action)
12578 actual_player_action = stored_player[i].programmed_action;
12580 PlayerActions(&stored_player[i], actual_player_action);
12582 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12585 ScrollScreen(NULL, SCROLL_GO_ON);
12587 /* for backwards compatibility, the following code emulates a fixed bug that
12588 occured when pushing elements (causing elements that just made their last
12589 pushing step to already (if possible) make their first falling step in the
12590 same game frame, which is bad); this code is also needed to use the famous
12591 "spring push bug" which is used in older levels and might be wanted to be
12592 used also in newer levels, but in this case the buggy pushing code is only
12593 affecting the "spring" element and no other elements */
12595 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12597 for (i = 0; i < MAX_PLAYERS; i++)
12599 struct PlayerInfo *player = &stored_player[i];
12600 int x = player->jx;
12601 int y = player->jy;
12603 if (player->active && player->is_pushing && player->is_moving &&
12605 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12606 Feld[x][y] == EL_SPRING))
12608 ContinueMoving(x, y);
12610 /* continue moving after pushing (this is actually a bug) */
12611 if (!IS_MOVING(x, y))
12612 Stop[x][y] = FALSE;
12618 debug_print_timestamp(0, "start main loop profiling");
12621 SCAN_PLAYFIELD(x, y)
12623 ChangeCount[x][y] = 0;
12624 ChangeEvent[x][y] = -1;
12626 /* this must be handled before main playfield loop */
12627 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12630 if (MovDelay[x][y] <= 0)
12634 #if USE_NEW_SNAP_DELAY
12635 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12638 if (MovDelay[x][y] <= 0)
12641 TEST_DrawLevelField(x, y);
12643 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12649 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12651 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12652 printf("GameActions(): This should never happen!\n");
12654 ChangePage[x][y] = -1;
12658 Stop[x][y] = FALSE;
12659 if (WasJustMoving[x][y] > 0)
12660 WasJustMoving[x][y]--;
12661 if (WasJustFalling[x][y] > 0)
12662 WasJustFalling[x][y]--;
12663 if (CheckCollision[x][y] > 0)
12664 CheckCollision[x][y]--;
12665 if (CheckImpact[x][y] > 0)
12666 CheckImpact[x][y]--;
12670 /* reset finished pushing action (not done in ContinueMoving() to allow
12671 continuous pushing animation for elements with zero push delay) */
12672 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12674 ResetGfxAnimation(x, y);
12675 TEST_DrawLevelField(x, y);
12679 if (IS_BLOCKED(x, y))
12683 Blocked2Moving(x, y, &oldx, &oldy);
12684 if (!IS_MOVING(oldx, oldy))
12686 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12687 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12688 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12689 printf("GameActions(): This should never happen!\n");
12696 debug_print_timestamp(0, "- time for pre-main loop:");
12699 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
12700 SCAN_PLAYFIELD(x, y)
12702 element = Feld[x][y];
12703 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12708 int element2 = element;
12709 int graphic2 = graphic;
12711 int element2 = Feld[x][y];
12712 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12714 int last_gfx_frame = GfxFrame[x][y];
12716 if (graphic_info[graphic2].anim_global_sync)
12717 GfxFrame[x][y] = FrameCounter;
12718 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12719 GfxFrame[x][y] = CustomValue[x][y];
12720 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12721 GfxFrame[x][y] = element_info[element2].collect_score;
12722 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12723 GfxFrame[x][y] = ChangeDelay[x][y];
12725 if (redraw && GfxFrame[x][y] != last_gfx_frame)
12726 DrawLevelGraphicAnimation(x, y, graphic2);
12729 ResetGfxFrame(x, y, TRUE);
12733 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12734 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12735 ResetRandomAnimationValue(x, y);
12739 SetRandomAnimationValue(x, y);
12743 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12746 #endif // -------------------- !!! TEST ONLY !!! --------------------
12749 debug_print_timestamp(0, "- time for TEST loop: -->");
12752 SCAN_PLAYFIELD(x, y)
12754 element = Feld[x][y];
12755 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12757 ResetGfxFrame(x, y, TRUE);
12759 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12760 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12761 ResetRandomAnimationValue(x, y);
12763 SetRandomAnimationValue(x, y);
12765 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12767 if (IS_INACTIVE(element))
12769 if (IS_ANIMATED(graphic))
12770 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12775 /* this may take place after moving, so 'element' may have changed */
12776 if (IS_CHANGING(x, y) &&
12777 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12779 int page = element_info[element].event_page_nr[CE_DELAY];
12782 HandleElementChange(x, y, page);
12784 if (CAN_CHANGE(element))
12785 HandleElementChange(x, y, page);
12787 if (HAS_ACTION(element))
12788 ExecuteCustomElementAction(x, y, element, page);
12791 element = Feld[x][y];
12792 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12795 #if 0 // ---------------------------------------------------------------------
12797 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12801 element = Feld[x][y];
12802 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12804 if (IS_ANIMATED(graphic) &&
12805 !IS_MOVING(x, y) &&
12807 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12809 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12810 TEST_DrawTwinkleOnField(x, y);
12812 else if (IS_MOVING(x, y))
12813 ContinueMoving(x, y);
12820 case EL_EM_EXIT_OPEN:
12821 case EL_SP_EXIT_OPEN:
12822 case EL_STEEL_EXIT_OPEN:
12823 case EL_EM_STEEL_EXIT_OPEN:
12824 case EL_SP_TERMINAL:
12825 case EL_SP_TERMINAL_ACTIVE:
12826 case EL_EXTRA_TIME:
12827 case EL_SHIELD_NORMAL:
12828 case EL_SHIELD_DEADLY:
12829 if (IS_ANIMATED(graphic))
12830 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12833 case EL_DYNAMITE_ACTIVE:
12834 case EL_EM_DYNAMITE_ACTIVE:
12835 case EL_DYNABOMB_PLAYER_1_ACTIVE:
12836 case EL_DYNABOMB_PLAYER_2_ACTIVE:
12837 case EL_DYNABOMB_PLAYER_3_ACTIVE:
12838 case EL_DYNABOMB_PLAYER_4_ACTIVE:
12839 case EL_SP_DISK_RED_ACTIVE:
12840 CheckDynamite(x, y);
12843 case EL_AMOEBA_GROWING:
12844 AmoebeWaechst(x, y);
12847 case EL_AMOEBA_SHRINKING:
12848 AmoebaDisappearing(x, y);
12851 #if !USE_NEW_AMOEBA_CODE
12852 case EL_AMOEBA_WET:
12853 case EL_AMOEBA_DRY:
12854 case EL_AMOEBA_FULL:
12856 case EL_EMC_DRIPPER:
12857 AmoebeAbleger(x, y);
12861 case EL_GAME_OF_LIFE:
12866 case EL_EXIT_CLOSED:
12870 case EL_EM_EXIT_CLOSED:
12874 case EL_STEEL_EXIT_CLOSED:
12875 CheckExitSteel(x, y);
12878 case EL_EM_STEEL_EXIT_CLOSED:
12879 CheckExitSteelEM(x, y);
12882 case EL_SP_EXIT_CLOSED:
12886 case EL_EXPANDABLE_WALL_GROWING:
12887 case EL_EXPANDABLE_STEELWALL_GROWING:
12888 MauerWaechst(x, y);
12891 case EL_EXPANDABLE_WALL:
12892 case EL_EXPANDABLE_WALL_HORIZONTAL:
12893 case EL_EXPANDABLE_WALL_VERTICAL:
12894 case EL_EXPANDABLE_WALL_ANY:
12895 case EL_BD_EXPANDABLE_WALL:
12896 MauerAbleger(x, y);
12899 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12900 case EL_EXPANDABLE_STEELWALL_VERTICAL:
12901 case EL_EXPANDABLE_STEELWALL_ANY:
12902 MauerAblegerStahl(x, y);
12906 CheckForDragon(x, y);
12912 case EL_ELEMENT_SNAPPING:
12913 case EL_DIAGONAL_SHRINKING:
12914 case EL_DIAGONAL_GROWING:
12917 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12919 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12924 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12925 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12930 #else // ---------------------------------------------------------------------
12932 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12936 element = Feld[x][y];
12937 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12939 if (IS_ANIMATED(graphic) &&
12940 !IS_MOVING(x, y) &&
12942 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12944 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12945 TEST_DrawTwinkleOnField(x, y);
12947 else if ((element == EL_ACID ||
12948 element == EL_EXIT_OPEN ||
12949 element == EL_EM_EXIT_OPEN ||
12950 element == EL_SP_EXIT_OPEN ||
12951 element == EL_STEEL_EXIT_OPEN ||
12952 element == EL_EM_STEEL_EXIT_OPEN ||
12953 element == EL_SP_TERMINAL ||
12954 element == EL_SP_TERMINAL_ACTIVE ||
12955 element == EL_EXTRA_TIME ||
12956 element == EL_SHIELD_NORMAL ||
12957 element == EL_SHIELD_DEADLY) &&
12958 IS_ANIMATED(graphic))
12959 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12960 else if (IS_MOVING(x, y))
12961 ContinueMoving(x, y);
12962 else if (IS_ACTIVE_BOMB(element))
12963 CheckDynamite(x, y);
12964 else if (element == EL_AMOEBA_GROWING)
12965 AmoebeWaechst(x, y);
12966 else if (element == EL_AMOEBA_SHRINKING)
12967 AmoebaDisappearing(x, y);
12969 #if !USE_NEW_AMOEBA_CODE
12970 else if (IS_AMOEBALIVE(element))
12971 AmoebeAbleger(x, y);
12974 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12976 else if (element == EL_EXIT_CLOSED)
12978 else if (element == EL_EM_EXIT_CLOSED)
12980 else if (element == EL_STEEL_EXIT_CLOSED)
12981 CheckExitSteel(x, y);
12982 else if (element == EL_EM_STEEL_EXIT_CLOSED)
12983 CheckExitSteelEM(x, y);
12984 else if (element == EL_SP_EXIT_CLOSED)
12986 else if (element == EL_EXPANDABLE_WALL_GROWING ||
12987 element == EL_EXPANDABLE_STEELWALL_GROWING)
12988 MauerWaechst(x, y);
12989 else if (element == EL_EXPANDABLE_WALL ||
12990 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12991 element == EL_EXPANDABLE_WALL_VERTICAL ||
12992 element == EL_EXPANDABLE_WALL_ANY ||
12993 element == EL_BD_EXPANDABLE_WALL)
12994 MauerAbleger(x, y);
12995 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12996 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12997 element == EL_EXPANDABLE_STEELWALL_ANY)
12998 MauerAblegerStahl(x, y);
12999 else if (element == EL_FLAMES)
13000 CheckForDragon(x, y);
13001 else if (element == EL_EXPLOSION)
13002 ; /* drawing of correct explosion animation is handled separately */
13003 else if (element == EL_ELEMENT_SNAPPING ||
13004 element == EL_DIAGONAL_SHRINKING ||
13005 element == EL_DIAGONAL_GROWING)
13007 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
13009 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13011 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
13012 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13014 #endif // ---------------------------------------------------------------------
13016 if (IS_BELT_ACTIVE(element))
13017 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
13019 if (game.magic_wall_active)
13021 int jx = local_player->jx, jy = local_player->jy;
13023 /* play the element sound at the position nearest to the player */
13024 if ((element == EL_MAGIC_WALL_FULL ||
13025 element == EL_MAGIC_WALL_ACTIVE ||
13026 element == EL_MAGIC_WALL_EMPTYING ||
13027 element == EL_BD_MAGIC_WALL_FULL ||
13028 element == EL_BD_MAGIC_WALL_ACTIVE ||
13029 element == EL_BD_MAGIC_WALL_EMPTYING ||
13030 element == EL_DC_MAGIC_WALL_FULL ||
13031 element == EL_DC_MAGIC_WALL_ACTIVE ||
13032 element == EL_DC_MAGIC_WALL_EMPTYING) &&
13033 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
13042 debug_print_timestamp(0, "- time for MAIN loop: -->");
13045 #if USE_NEW_AMOEBA_CODE
13046 /* new experimental amoeba growth stuff */
13047 if (!(FrameCounter % 8))
13049 static unsigned long random = 1684108901;
13051 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
13053 x = RND(lev_fieldx);
13054 y = RND(lev_fieldy);
13055 element = Feld[x][y];
13057 if (!IS_PLAYER(x,y) &&
13058 (element == EL_EMPTY ||
13059 CAN_GROW_INTO(element) ||
13060 element == EL_QUICKSAND_EMPTY ||
13061 element == EL_QUICKSAND_FAST_EMPTY ||
13062 element == EL_ACID_SPLASH_LEFT ||
13063 element == EL_ACID_SPLASH_RIGHT))
13065 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
13066 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
13067 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
13068 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
13069 Feld[x][y] = EL_AMOEBA_DROP;
13072 random = random * 129 + 1;
13078 if (game.explosions_delayed)
13081 game.explosions_delayed = FALSE;
13083 SCAN_PLAYFIELD(x, y)
13085 element = Feld[x][y];
13087 if (ExplodeField[x][y])
13088 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
13089 else if (element == EL_EXPLOSION)
13090 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
13092 ExplodeField[x][y] = EX_TYPE_NONE;
13095 game.explosions_delayed = TRUE;
13098 if (game.magic_wall_active)
13100 if (!(game.magic_wall_time_left % 4))
13102 int element = Feld[magic_wall_x][magic_wall_y];
13104 if (element == EL_BD_MAGIC_WALL_FULL ||
13105 element == EL_BD_MAGIC_WALL_ACTIVE ||
13106 element == EL_BD_MAGIC_WALL_EMPTYING)
13107 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
13108 else if (element == EL_DC_MAGIC_WALL_FULL ||
13109 element == EL_DC_MAGIC_WALL_ACTIVE ||
13110 element == EL_DC_MAGIC_WALL_EMPTYING)
13111 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
13113 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
13116 if (game.magic_wall_time_left > 0)
13118 game.magic_wall_time_left--;
13120 if (!game.magic_wall_time_left)
13122 SCAN_PLAYFIELD(x, y)
13124 element = Feld[x][y];
13126 if (element == EL_MAGIC_WALL_ACTIVE ||
13127 element == EL_MAGIC_WALL_FULL)
13129 Feld[x][y] = EL_MAGIC_WALL_DEAD;
13130 TEST_DrawLevelField(x, y);
13132 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
13133 element == EL_BD_MAGIC_WALL_FULL)
13135 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
13136 TEST_DrawLevelField(x, y);
13138 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
13139 element == EL_DC_MAGIC_WALL_FULL)
13141 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
13142 TEST_DrawLevelField(x, y);
13146 game.magic_wall_active = FALSE;
13151 if (game.light_time_left > 0)
13153 game.light_time_left--;
13155 if (game.light_time_left == 0)
13156 RedrawAllLightSwitchesAndInvisibleElements();
13159 if (game.timegate_time_left > 0)
13161 game.timegate_time_left--;
13163 if (game.timegate_time_left == 0)
13164 CloseAllOpenTimegates();
13167 if (game.lenses_time_left > 0)
13169 game.lenses_time_left--;
13171 if (game.lenses_time_left == 0)
13172 RedrawAllInvisibleElementsForLenses();
13175 if (game.magnify_time_left > 0)
13177 game.magnify_time_left--;
13179 if (game.magnify_time_left == 0)
13180 RedrawAllInvisibleElementsForMagnifier();
13183 for (i = 0; i < MAX_PLAYERS; i++)
13185 struct PlayerInfo *player = &stored_player[i];
13187 if (SHIELD_ON(player))
13189 if (player->shield_deadly_time_left)
13190 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13191 else if (player->shield_normal_time_left)
13192 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13196 #if USE_DELAYED_GFX_REDRAW
13197 SCAN_PLAYFIELD(x, y)
13200 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13202 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13203 GfxRedraw[x][y] != GFX_REDRAW_NONE)
13206 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13207 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13209 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13210 DrawLevelField(x, y);
13212 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13213 DrawLevelFieldCrumbled(x, y);
13215 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13216 DrawLevelFieldCrumbledNeighbours(x, y);
13218 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13219 DrawTwinkleOnField(x, y);
13222 GfxRedraw[x][y] = GFX_REDRAW_NONE;
13229 PlayAllPlayersSound();
13231 if (options.debug) /* calculate frames per second */
13233 static unsigned long fps_counter = 0;
13234 static int fps_frames = 0;
13235 unsigned long fps_delay_ms = Counter() - fps_counter;
13239 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
13241 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13244 fps_counter = Counter();
13247 redraw_mask |= REDRAW_FPS;
13250 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
13252 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13254 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13256 local_player->show_envelope = 0;
13260 debug_print_timestamp(0, "stop main loop profiling ");
13261 printf("----------------------------------------------------------\n");
13264 /* use random number generator in every frame to make it less predictable */
13265 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13269 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13271 int min_x = x, min_y = y, max_x = x, max_y = y;
13274 for (i = 0; i < MAX_PLAYERS; i++)
13276 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13278 if (!stored_player[i].active || &stored_player[i] == player)
13281 min_x = MIN(min_x, jx);
13282 min_y = MIN(min_y, jy);
13283 max_x = MAX(max_x, jx);
13284 max_y = MAX(max_y, jy);
13287 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13290 static boolean AllPlayersInVisibleScreen()
13294 for (i = 0; i < MAX_PLAYERS; i++)
13296 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13298 if (!stored_player[i].active)
13301 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13308 void ScrollLevel(int dx, int dy)
13311 /* (directly solved in BlitBitmap() now) */
13312 static Bitmap *bitmap_db_field2 = NULL;
13313 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13320 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13321 /* only horizontal XOR vertical scroll direction allowed */
13322 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13327 /* (directly solved in BlitBitmap() now) */
13328 if (bitmap_db_field2 == NULL)
13329 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13331 /* needed when blitting directly to same bitmap -- should not be needed with
13332 recent SDL libraries, but apparently does not work in 1.2.11 directly */
13333 BlitBitmap(drawto_field, bitmap_db_field2,
13334 FX + TILEX * (dx == -1) - softscroll_offset,
13335 FY + TILEY * (dy == -1) - softscroll_offset,
13336 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13337 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13338 FX + TILEX * (dx == 1) - softscroll_offset,
13339 FY + TILEY * (dy == 1) - softscroll_offset);
13340 BlitBitmap(bitmap_db_field2, drawto_field,
13341 FX + TILEX * (dx == 1) - softscroll_offset,
13342 FY + TILEY * (dy == 1) - softscroll_offset,
13343 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13344 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13345 FX + TILEX * (dx == 1) - softscroll_offset,
13346 FY + TILEY * (dy == 1) - softscroll_offset);
13351 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13352 int xsize = (BX2 - BX1 + 1);
13353 int ysize = (BY2 - BY1 + 1);
13354 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13355 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13356 int step = (start < end ? +1 : -1);
13358 for (i = start; i != end; i += step)
13360 BlitBitmap(drawto_field, drawto_field,
13361 FX + TILEX * (dx != 0 ? i + step : 0),
13362 FY + TILEY * (dy != 0 ? i + step : 0),
13363 TILEX * (dx != 0 ? 1 : xsize),
13364 TILEY * (dy != 0 ? 1 : ysize),
13365 FX + TILEX * (dx != 0 ? i : 0),
13366 FY + TILEY * (dy != 0 ? i : 0));
13371 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13373 BlitBitmap(drawto_field, drawto_field,
13374 FX + TILEX * (dx == -1) - softscroll_offset,
13375 FY + TILEY * (dy == -1) - softscroll_offset,
13376 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13377 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13378 FX + TILEX * (dx == 1) - softscroll_offset,
13379 FY + TILEY * (dy == 1) - softscroll_offset);
13385 x = (dx == 1 ? BX1 : BX2);
13386 for (y = BY1; y <= BY2; y++)
13387 DrawScreenField(x, y);
13392 y = (dy == 1 ? BY1 : BY2);
13393 for (x = BX1; x <= BX2; x++)
13394 DrawScreenField(x, y);
13397 redraw_mask |= REDRAW_FIELD;
13400 static boolean canFallDown(struct PlayerInfo *player)
13402 int jx = player->jx, jy = player->jy;
13404 return (IN_LEV_FIELD(jx, jy + 1) &&
13405 (IS_FREE(jx, jy + 1) ||
13406 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13407 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13408 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13411 static boolean canPassField(int x, int y, int move_dir)
13413 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13414 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13415 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13416 int nextx = x + dx;
13417 int nexty = y + dy;
13418 int element = Feld[x][y];
13420 return (IS_PASSABLE_FROM(element, opposite_dir) &&
13421 !CAN_MOVE(element) &&
13422 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13423 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13424 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13427 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13429 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13430 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13431 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13435 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13436 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13437 (IS_DIGGABLE(Feld[newx][newy]) ||
13438 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13439 canPassField(newx, newy, move_dir)));
13442 static void CheckGravityMovement(struct PlayerInfo *player)
13444 #if USE_PLAYER_GRAVITY
13445 if (player->gravity && !player->programmed_action)
13447 if (game.gravity && !player->programmed_action)
13450 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13451 int move_dir_vertical = player->effective_action & MV_VERTICAL;
13452 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13453 int jx = player->jx, jy = player->jy;
13454 boolean player_is_moving_to_valid_field =
13455 (!player_is_snapping &&
13456 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13457 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13458 boolean player_can_fall_down = canFallDown(player);
13460 if (player_can_fall_down &&
13461 !player_is_moving_to_valid_field)
13462 player->programmed_action = MV_DOWN;
13466 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13468 return CheckGravityMovement(player);
13470 #if USE_PLAYER_GRAVITY
13471 if (player->gravity && !player->programmed_action)
13473 if (game.gravity && !player->programmed_action)
13476 int jx = player->jx, jy = player->jy;
13477 boolean field_under_player_is_free =
13478 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13479 boolean player_is_standing_on_valid_field =
13480 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13481 (IS_WALKABLE(Feld[jx][jy]) &&
13482 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13484 if (field_under_player_is_free && !player_is_standing_on_valid_field)
13485 player->programmed_action = MV_DOWN;
13490 MovePlayerOneStep()
13491 -----------------------------------------------------------------------------
13492 dx, dy: direction (non-diagonal) to try to move the player to
13493 real_dx, real_dy: direction as read from input device (can be diagonal)
13496 boolean MovePlayerOneStep(struct PlayerInfo *player,
13497 int dx, int dy, int real_dx, int real_dy)
13499 int jx = player->jx, jy = player->jy;
13500 int new_jx = jx + dx, new_jy = jy + dy;
13501 #if !USE_FIXED_DONT_RUN_INTO
13505 boolean player_can_move = !player->cannot_move;
13507 if (!player->active || (!dx && !dy))
13508 return MP_NO_ACTION;
13510 player->MovDir = (dx < 0 ? MV_LEFT :
13511 dx > 0 ? MV_RIGHT :
13513 dy > 0 ? MV_DOWN : MV_NONE);
13515 if (!IN_LEV_FIELD(new_jx, new_jy))
13516 return MP_NO_ACTION;
13518 if (!player_can_move)
13520 if (player->MovPos == 0)
13522 player->is_moving = FALSE;
13523 player->is_digging = FALSE;
13524 player->is_collecting = FALSE;
13525 player->is_snapping = FALSE;
13526 player->is_pushing = FALSE;
13531 if (!options.network && game.centered_player_nr == -1 &&
13532 !AllPlayersInSight(player, new_jx, new_jy))
13533 return MP_NO_ACTION;
13535 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13536 return MP_NO_ACTION;
13539 #if !USE_FIXED_DONT_RUN_INTO
13540 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13542 /* (moved to DigField()) */
13543 if (player_can_move && DONT_RUN_INTO(element))
13545 if (element == EL_ACID && dx == 0 && dy == 1)
13547 SplashAcid(new_jx, new_jy);
13548 Feld[jx][jy] = EL_PLAYER_1;
13549 InitMovingField(jx, jy, MV_DOWN);
13550 Store[jx][jy] = EL_ACID;
13551 ContinueMoving(jx, jy);
13552 BuryPlayer(player);
13555 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13561 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13562 if (can_move != MP_MOVING)
13565 /* check if DigField() has caused relocation of the player */
13566 if (player->jx != jx || player->jy != jy)
13567 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13569 StorePlayer[jx][jy] = 0;
13570 player->last_jx = jx;
13571 player->last_jy = jy;
13572 player->jx = new_jx;
13573 player->jy = new_jy;
13574 StorePlayer[new_jx][new_jy] = player->element_nr;
13576 if (player->move_delay_value_next != -1)
13578 player->move_delay_value = player->move_delay_value_next;
13579 player->move_delay_value_next = -1;
13583 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13585 player->step_counter++;
13587 PlayerVisit[jx][jy] = FrameCounter;
13589 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13590 player->is_moving = TRUE;
13594 /* should better be called in MovePlayer(), but this breaks some tapes */
13595 ScrollPlayer(player, SCROLL_INIT);
13601 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13603 int jx = player->jx, jy = player->jy;
13604 int old_jx = jx, old_jy = jy;
13605 int moved = MP_NO_ACTION;
13607 if (!player->active)
13612 if (player->MovPos == 0)
13614 player->is_moving = FALSE;
13615 player->is_digging = FALSE;
13616 player->is_collecting = FALSE;
13617 player->is_snapping = FALSE;
13618 player->is_pushing = FALSE;
13624 if (player->move_delay > 0)
13627 player->move_delay = -1; /* set to "uninitialized" value */
13629 /* store if player is automatically moved to next field */
13630 player->is_auto_moving = (player->programmed_action != MV_NONE);
13632 /* remove the last programmed player action */
13633 player->programmed_action = 0;
13635 if (player->MovPos)
13637 /* should only happen if pre-1.2 tape recordings are played */
13638 /* this is only for backward compatibility */
13640 int original_move_delay_value = player->move_delay_value;
13643 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
13647 /* scroll remaining steps with finest movement resolution */
13648 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13650 while (player->MovPos)
13652 ScrollPlayer(player, SCROLL_GO_ON);
13653 ScrollScreen(NULL, SCROLL_GO_ON);
13655 AdvanceFrameAndPlayerCounters(player->index_nr);
13661 player->move_delay_value = original_move_delay_value;
13664 player->is_active = FALSE;
13666 if (player->last_move_dir & MV_HORIZONTAL)
13668 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13669 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13673 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13674 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13677 #if USE_FIXED_BORDER_RUNNING_GFX
13678 if (!moved && !player->is_active)
13680 player->is_moving = FALSE;
13681 player->is_digging = FALSE;
13682 player->is_collecting = FALSE;
13683 player->is_snapping = FALSE;
13684 player->is_pushing = FALSE;
13692 if (moved & MP_MOVING && !ScreenMovPos &&
13693 (player->index_nr == game.centered_player_nr ||
13694 game.centered_player_nr == -1))
13696 if (moved & MP_MOVING && !ScreenMovPos &&
13697 (player == local_player || !options.network))
13700 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13701 int offset = game.scroll_delay_value;
13703 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13705 /* actual player has left the screen -- scroll in that direction */
13706 if (jx != old_jx) /* player has moved horizontally */
13707 scroll_x += (jx - old_jx);
13708 else /* player has moved vertically */
13709 scroll_y += (jy - old_jy);
13713 if (jx != old_jx) /* player has moved horizontally */
13715 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
13716 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13717 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13719 /* don't scroll over playfield boundaries */
13720 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13721 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13723 /* don't scroll more than one field at a time */
13724 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13726 /* don't scroll against the player's moving direction */
13727 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
13728 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13729 scroll_x = old_scroll_x;
13731 else /* player has moved vertically */
13733 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
13734 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13735 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13737 /* don't scroll over playfield boundaries */
13738 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13739 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13741 /* don't scroll more than one field at a time */
13742 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13744 /* don't scroll against the player's moving direction */
13745 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
13746 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13747 scroll_y = old_scroll_y;
13751 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13754 if (!options.network && game.centered_player_nr == -1 &&
13755 !AllPlayersInVisibleScreen())
13757 scroll_x = old_scroll_x;
13758 scroll_y = old_scroll_y;
13762 if (!options.network && !AllPlayersInVisibleScreen())
13764 scroll_x = old_scroll_x;
13765 scroll_y = old_scroll_y;
13770 ScrollScreen(player, SCROLL_INIT);
13771 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13776 player->StepFrame = 0;
13778 if (moved & MP_MOVING)
13780 if (old_jx != jx && old_jy == jy)
13781 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13782 else if (old_jx == jx && old_jy != jy)
13783 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13785 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
13787 player->last_move_dir = player->MovDir;
13788 player->is_moving = TRUE;
13789 player->is_snapping = FALSE;
13790 player->is_switching = FALSE;
13791 player->is_dropping = FALSE;
13792 player->is_dropping_pressed = FALSE;
13793 player->drop_pressed_delay = 0;
13796 /* should better be called here than above, but this breaks some tapes */
13797 ScrollPlayer(player, SCROLL_INIT);
13802 CheckGravityMovementWhenNotMoving(player);
13804 player->is_moving = FALSE;
13806 /* at this point, the player is allowed to move, but cannot move right now
13807 (e.g. because of something blocking the way) -- ensure that the player
13808 is also allowed to move in the next frame (in old versions before 3.1.1,
13809 the player was forced to wait again for eight frames before next try) */
13811 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13812 player->move_delay = 0; /* allow direct movement in the next frame */
13815 if (player->move_delay == -1) /* not yet initialized by DigField() */
13816 player->move_delay = player->move_delay_value;
13818 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13820 TestIfPlayerTouchesBadThing(jx, jy);
13821 TestIfPlayerTouchesCustomElement(jx, jy);
13824 if (!player->active)
13825 RemovePlayer(player);
13830 void ScrollPlayer(struct PlayerInfo *player, int mode)
13832 int jx = player->jx, jy = player->jy;
13833 int last_jx = player->last_jx, last_jy = player->last_jy;
13834 int move_stepsize = TILEX / player->move_delay_value;
13836 #if USE_NEW_PLAYER_SPEED
13837 if (!player->active)
13840 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
13843 if (!player->active || player->MovPos == 0)
13847 if (mode == SCROLL_INIT)
13849 player->actual_frame_counter = FrameCounter;
13850 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13852 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13853 Feld[last_jx][last_jy] == EL_EMPTY)
13855 int last_field_block_delay = 0; /* start with no blocking at all */
13856 int block_delay_adjustment = player->block_delay_adjustment;
13858 /* if player blocks last field, add delay for exactly one move */
13859 if (player->block_last_field)
13861 last_field_block_delay += player->move_delay_value;
13863 /* when blocking enabled, prevent moving up despite gravity */
13864 #if USE_PLAYER_GRAVITY
13865 if (player->gravity && player->MovDir == MV_UP)
13866 block_delay_adjustment = -1;
13868 if (game.gravity && player->MovDir == MV_UP)
13869 block_delay_adjustment = -1;
13873 /* add block delay adjustment (also possible when not blocking) */
13874 last_field_block_delay += block_delay_adjustment;
13876 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13877 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13880 #if USE_NEW_PLAYER_SPEED
13881 if (player->MovPos != 0) /* player has not yet reached destination */
13887 else if (!FrameReached(&player->actual_frame_counter, 1))
13890 #if USE_NEW_PLAYER_SPEED
13891 if (player->MovPos != 0)
13893 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13894 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13896 /* before DrawPlayer() to draw correct player graphic for this case */
13897 if (player->MovPos == 0)
13898 CheckGravityMovement(player);
13901 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13902 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13904 /* before DrawPlayer() to draw correct player graphic for this case */
13905 if (player->MovPos == 0)
13906 CheckGravityMovement(player);
13909 if (player->MovPos == 0) /* player reached destination field */
13911 if (player->move_delay_reset_counter > 0)
13913 player->move_delay_reset_counter--;
13915 if (player->move_delay_reset_counter == 0)
13917 /* continue with normal speed after quickly moving through gate */
13918 HALVE_PLAYER_SPEED(player);
13920 /* be able to make the next move without delay */
13921 player->move_delay = 0;
13925 player->last_jx = jx;
13926 player->last_jy = jy;
13928 if (Feld[jx][jy] == EL_EXIT_OPEN ||
13929 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13931 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
13933 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13934 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13936 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13938 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13939 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
13941 DrawPlayer(player); /* needed here only to cleanup last field */
13942 RemovePlayer(player);
13944 if (local_player->friends_still_needed == 0 ||
13945 IS_SP_ELEMENT(Feld[jx][jy]))
13946 PlayerWins(player);
13949 /* this breaks one level: "machine", level 000 */
13951 int move_direction = player->MovDir;
13952 int enter_side = MV_DIR_OPPOSITE(move_direction);
13953 int leave_side = move_direction;
13954 int old_jx = last_jx;
13955 int old_jy = last_jy;
13956 int old_element = Feld[old_jx][old_jy];
13957 int new_element = Feld[jx][jy];
13959 if (IS_CUSTOM_ELEMENT(old_element))
13960 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13962 player->index_bit, leave_side);
13964 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13965 CE_PLAYER_LEAVES_X,
13966 player->index_bit, leave_side);
13968 if (IS_CUSTOM_ELEMENT(new_element))
13969 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13970 player->index_bit, enter_side);
13972 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13973 CE_PLAYER_ENTERS_X,
13974 player->index_bit, enter_side);
13976 #if USE_FIX_CE_ACTION_WITH_PLAYER
13977 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13978 CE_MOVE_OF_X, move_direction);
13980 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13981 CE_MOVE_OF_X, move_direction);
13985 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13987 TestIfPlayerTouchesBadThing(jx, jy);
13988 TestIfPlayerTouchesCustomElement(jx, jy);
13990 /* needed because pushed element has not yet reached its destination,
13991 so it would trigger a change event at its previous field location */
13992 if (!player->is_pushing)
13993 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
13995 if (!player->active)
13996 RemovePlayer(player);
13999 if (!local_player->LevelSolved && level.use_step_counter)
14009 if (TimeLeft <= 10 && setup.time_limit)
14010 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14013 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14015 DisplayGameControlValues();
14017 DrawGameValue_Time(TimeLeft);
14020 if (!TimeLeft && setup.time_limit)
14021 for (i = 0; i < MAX_PLAYERS; i++)
14022 KillPlayer(&stored_player[i]);
14025 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
14027 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
14029 DisplayGameControlValues();
14032 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
14033 DrawGameValue_Time(TimePlayed);
14037 if (tape.single_step && tape.recording && !tape.pausing &&
14038 !player->programmed_action)
14039 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
14043 void ScrollScreen(struct PlayerInfo *player, int mode)
14045 static unsigned long screen_frame_counter = 0;
14047 if (mode == SCROLL_INIT)
14049 /* set scrolling step size according to actual player's moving speed */
14050 ScrollStepSize = TILEX / player->move_delay_value;
14052 screen_frame_counter = FrameCounter;
14053 ScreenMovDir = player->MovDir;
14054 ScreenMovPos = player->MovPos;
14055 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14058 else if (!FrameReached(&screen_frame_counter, 1))
14063 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
14064 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14065 redraw_mask |= REDRAW_FIELD;
14068 ScreenMovDir = MV_NONE;
14071 void TestIfPlayerTouchesCustomElement(int x, int y)
14073 static int xy[4][2] =
14080 static int trigger_sides[4][2] =
14082 /* center side border side */
14083 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14084 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14085 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14086 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14088 static int touch_dir[4] =
14090 MV_LEFT | MV_RIGHT,
14095 int center_element = Feld[x][y]; /* should always be non-moving! */
14098 for (i = 0; i < NUM_DIRECTIONS; i++)
14100 int xx = x + xy[i][0];
14101 int yy = y + xy[i][1];
14102 int center_side = trigger_sides[i][0];
14103 int border_side = trigger_sides[i][1];
14104 int border_element;
14106 if (!IN_LEV_FIELD(xx, yy))
14109 if (IS_PLAYER(x, y)) /* player found at center element */
14111 struct PlayerInfo *player = PLAYERINFO(x, y);
14113 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14114 border_element = Feld[xx][yy]; /* may be moving! */
14115 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14116 border_element = Feld[xx][yy];
14117 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14118 border_element = MovingOrBlocked2Element(xx, yy);
14120 continue; /* center and border element do not touch */
14122 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
14123 player->index_bit, border_side);
14124 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
14125 CE_PLAYER_TOUCHES_X,
14126 player->index_bit, border_side);
14128 #if USE_FIX_CE_ACTION_WITH_PLAYER
14130 /* use player element that is initially defined in the level playfield,
14131 not the player element that corresponds to the runtime player number
14132 (example: a level that contains EL_PLAYER_3 as the only player would
14133 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14134 int player_element = PLAYERINFO(x, y)->initial_element;
14136 CheckElementChangeBySide(xx, yy, border_element, player_element,
14137 CE_TOUCHING_X, border_side);
14141 else if (IS_PLAYER(xx, yy)) /* player found at border element */
14143 struct PlayerInfo *player = PLAYERINFO(xx, yy);
14145 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14147 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14148 continue; /* center and border element do not touch */
14151 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14152 player->index_bit, center_side);
14153 CheckTriggeredElementChangeByPlayer(x, y, center_element,
14154 CE_PLAYER_TOUCHES_X,
14155 player->index_bit, center_side);
14157 #if USE_FIX_CE_ACTION_WITH_PLAYER
14159 /* use player element that is initially defined in the level playfield,
14160 not the player element that corresponds to the runtime player number
14161 (example: a level that contains EL_PLAYER_3 as the only player would
14162 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14163 int player_element = PLAYERINFO(xx, yy)->initial_element;
14165 CheckElementChangeBySide(x, y, center_element, player_element,
14166 CE_TOUCHING_X, center_side);
14175 #if USE_ELEMENT_TOUCHING_BUGFIX
14177 void TestIfElementTouchesCustomElement(int x, int y)
14179 static int xy[4][2] =
14186 static int trigger_sides[4][2] =
14188 /* center side border side */
14189 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14190 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14191 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14192 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14194 static int touch_dir[4] =
14196 MV_LEFT | MV_RIGHT,
14201 boolean change_center_element = FALSE;
14202 int center_element = Feld[x][y]; /* should always be non-moving! */
14203 int border_element_old[NUM_DIRECTIONS];
14206 for (i = 0; i < NUM_DIRECTIONS; i++)
14208 int xx = x + xy[i][0];
14209 int yy = y + xy[i][1];
14210 int border_element;
14212 border_element_old[i] = -1;
14214 if (!IN_LEV_FIELD(xx, yy))
14217 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14218 border_element = Feld[xx][yy]; /* may be moving! */
14219 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14220 border_element = Feld[xx][yy];
14221 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14222 border_element = MovingOrBlocked2Element(xx, yy);
14224 continue; /* center and border element do not touch */
14226 border_element_old[i] = border_element;
14229 for (i = 0; i < NUM_DIRECTIONS; i++)
14231 int xx = x + xy[i][0];
14232 int yy = y + xy[i][1];
14233 int center_side = trigger_sides[i][0];
14234 int border_element = border_element_old[i];
14236 if (border_element == -1)
14239 /* check for change of border element */
14240 CheckElementChangeBySide(xx, yy, border_element, center_element,
14241 CE_TOUCHING_X, center_side);
14243 /* (center element cannot be player, so we dont have to check this here) */
14246 for (i = 0; i < NUM_DIRECTIONS; i++)
14248 int xx = x + xy[i][0];
14249 int yy = y + xy[i][1];
14250 int border_side = trigger_sides[i][1];
14251 int border_element = border_element_old[i];
14253 if (border_element == -1)
14256 /* check for change of center element (but change it only once) */
14257 if (!change_center_element)
14258 change_center_element =
14259 CheckElementChangeBySide(x, y, center_element, border_element,
14260 CE_TOUCHING_X, border_side);
14262 #if USE_FIX_CE_ACTION_WITH_PLAYER
14263 if (IS_PLAYER(xx, yy))
14265 /* use player element that is initially defined in the level playfield,
14266 not the player element that corresponds to the runtime player number
14267 (example: a level that contains EL_PLAYER_3 as the only player would
14268 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14269 int player_element = PLAYERINFO(xx, yy)->initial_element;
14271 CheckElementChangeBySide(x, y, center_element, player_element,
14272 CE_TOUCHING_X, border_side);
14280 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14282 static int xy[4][2] =
14289 static int trigger_sides[4][2] =
14291 /* center side border side */
14292 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14293 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14294 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14295 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14297 static int touch_dir[4] =
14299 MV_LEFT | MV_RIGHT,
14304 boolean change_center_element = FALSE;
14305 int center_element = Feld[x][y]; /* should always be non-moving! */
14308 for (i = 0; i < NUM_DIRECTIONS; i++)
14310 int xx = x + xy[i][0];
14311 int yy = y + xy[i][1];
14312 int center_side = trigger_sides[i][0];
14313 int border_side = trigger_sides[i][1];
14314 int border_element;
14316 if (!IN_LEV_FIELD(xx, yy))
14319 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14320 border_element = Feld[xx][yy]; /* may be moving! */
14321 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14322 border_element = Feld[xx][yy];
14323 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14324 border_element = MovingOrBlocked2Element(xx, yy);
14326 continue; /* center and border element do not touch */
14328 /* check for change of center element (but change it only once) */
14329 if (!change_center_element)
14330 change_center_element =
14331 CheckElementChangeBySide(x, y, center_element, border_element,
14332 CE_TOUCHING_X, border_side);
14334 /* check for change of border element */
14335 CheckElementChangeBySide(xx, yy, border_element, center_element,
14336 CE_TOUCHING_X, center_side);
14342 void TestIfElementHitsCustomElement(int x, int y, int direction)
14344 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14345 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14346 int hitx = x + dx, hity = y + dy;
14347 int hitting_element = Feld[x][y];
14348 int touched_element;
14350 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14353 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14354 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14356 if (IN_LEV_FIELD(hitx, hity))
14358 int opposite_direction = MV_DIR_OPPOSITE(direction);
14359 int hitting_side = direction;
14360 int touched_side = opposite_direction;
14361 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14362 MovDir[hitx][hity] != direction ||
14363 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14369 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14370 CE_HITTING_X, touched_side);
14372 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14373 CE_HIT_BY_X, hitting_side);
14375 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14376 CE_HIT_BY_SOMETHING, opposite_direction);
14378 #if USE_FIX_CE_ACTION_WITH_PLAYER
14379 if (IS_PLAYER(hitx, hity))
14381 /* use player element that is initially defined in the level playfield,
14382 not the player element that corresponds to the runtime player number
14383 (example: a level that contains EL_PLAYER_3 as the only player would
14384 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14385 int player_element = PLAYERINFO(hitx, hity)->initial_element;
14387 CheckElementChangeBySide(x, y, hitting_element, player_element,
14388 CE_HITTING_X, touched_side);
14394 /* "hitting something" is also true when hitting the playfield border */
14395 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14396 CE_HITTING_SOMETHING, direction);
14400 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14402 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14403 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14404 int hitx = x + dx, hity = y + dy;
14405 int hitting_element = Feld[x][y];
14406 int touched_element;
14408 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14409 !IS_FREE(hitx, hity) &&
14410 (!IS_MOVING(hitx, hity) ||
14411 MovDir[hitx][hity] != direction ||
14412 ABS(MovPos[hitx][hity]) <= TILEY / 2));
14415 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14419 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14423 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14424 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14426 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14427 EP_CAN_SMASH_EVERYTHING, direction);
14429 if (IN_LEV_FIELD(hitx, hity))
14431 int opposite_direction = MV_DIR_OPPOSITE(direction);
14432 int hitting_side = direction;
14433 int touched_side = opposite_direction;
14435 int touched_element = MovingOrBlocked2Element(hitx, hity);
14438 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14439 MovDir[hitx][hity] != direction ||
14440 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14449 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14450 CE_SMASHED_BY_SOMETHING, opposite_direction);
14452 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14453 CE_OTHER_IS_SMASHING, touched_side);
14455 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14456 CE_OTHER_GETS_SMASHED, hitting_side);
14462 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14464 int i, kill_x = -1, kill_y = -1;
14466 int bad_element = -1;
14467 static int test_xy[4][2] =
14474 static int test_dir[4] =
14482 for (i = 0; i < NUM_DIRECTIONS; i++)
14484 int test_x, test_y, test_move_dir, test_element;
14486 test_x = good_x + test_xy[i][0];
14487 test_y = good_y + test_xy[i][1];
14489 if (!IN_LEV_FIELD(test_x, test_y))
14493 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14495 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14497 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14498 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14500 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14501 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
14505 bad_element = test_element;
14511 if (kill_x != -1 || kill_y != -1)
14513 if (IS_PLAYER(good_x, good_y))
14515 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14517 if (player->shield_deadly_time_left > 0 &&
14518 !IS_INDESTRUCTIBLE(bad_element))
14519 Bang(kill_x, kill_y);
14520 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14521 KillPlayer(player);
14524 Bang(good_x, good_y);
14528 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14530 int i, kill_x = -1, kill_y = -1;
14531 int bad_element = Feld[bad_x][bad_y];
14532 static int test_xy[4][2] =
14539 static int touch_dir[4] =
14541 MV_LEFT | MV_RIGHT,
14546 static int test_dir[4] =
14554 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
14557 for (i = 0; i < NUM_DIRECTIONS; i++)
14559 int test_x, test_y, test_move_dir, test_element;
14561 test_x = bad_x + test_xy[i][0];
14562 test_y = bad_y + test_xy[i][1];
14564 if (!IN_LEV_FIELD(test_x, test_y))
14568 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14570 test_element = Feld[test_x][test_y];
14572 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14573 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14575 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
14576 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
14578 /* good thing is player or penguin that does not move away */
14579 if (IS_PLAYER(test_x, test_y))
14581 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14583 if (bad_element == EL_ROBOT && player->is_moving)
14584 continue; /* robot does not kill player if he is moving */
14586 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14588 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14589 continue; /* center and border element do not touch */
14597 else if (test_element == EL_PENGUIN)
14607 if (kill_x != -1 || kill_y != -1)
14609 if (IS_PLAYER(kill_x, kill_y))
14611 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14613 if (player->shield_deadly_time_left > 0 &&
14614 !IS_INDESTRUCTIBLE(bad_element))
14615 Bang(bad_x, bad_y);
14616 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14617 KillPlayer(player);
14620 Bang(kill_x, kill_y);
14624 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14626 int bad_element = Feld[bad_x][bad_y];
14627 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14628 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
14629 int test_x = bad_x + dx, test_y = bad_y + dy;
14630 int test_move_dir, test_element;
14631 int kill_x = -1, kill_y = -1;
14633 if (!IN_LEV_FIELD(test_x, test_y))
14637 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14639 test_element = Feld[test_x][test_y];
14641 if (test_move_dir != bad_move_dir)
14643 /* good thing can be player or penguin that does not move away */
14644 if (IS_PLAYER(test_x, test_y))
14646 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14648 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14649 player as being hit when he is moving towards the bad thing, because
14650 the "get hit by" condition would be lost after the player stops) */
14651 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14652 return; /* player moves away from bad thing */
14657 else if (test_element == EL_PENGUIN)
14664 if (kill_x != -1 || kill_y != -1)
14666 if (IS_PLAYER(kill_x, kill_y))
14668 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14670 if (player->shield_deadly_time_left > 0 &&
14671 !IS_INDESTRUCTIBLE(bad_element))
14672 Bang(bad_x, bad_y);
14673 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14674 KillPlayer(player);
14677 Bang(kill_x, kill_y);
14681 void TestIfPlayerTouchesBadThing(int x, int y)
14683 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14686 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14688 TestIfGoodThingHitsBadThing(x, y, move_dir);
14691 void TestIfBadThingTouchesPlayer(int x, int y)
14693 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14696 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14698 TestIfBadThingHitsGoodThing(x, y, move_dir);
14701 void TestIfFriendTouchesBadThing(int x, int y)
14703 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14706 void TestIfBadThingTouchesFriend(int x, int y)
14708 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14711 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14713 int i, kill_x = bad_x, kill_y = bad_y;
14714 static int xy[4][2] =
14722 for (i = 0; i < NUM_DIRECTIONS; i++)
14726 x = bad_x + xy[i][0];
14727 y = bad_y + xy[i][1];
14728 if (!IN_LEV_FIELD(x, y))
14731 element = Feld[x][y];
14732 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14733 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14741 if (kill_x != bad_x || kill_y != bad_y)
14742 Bang(bad_x, bad_y);
14745 void KillPlayer(struct PlayerInfo *player)
14747 int jx = player->jx, jy = player->jy;
14749 if (!player->active)
14753 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14754 player->killed, player->active, player->reanimated);
14757 /* the following code was introduced to prevent an infinite loop when calling
14759 -> CheckTriggeredElementChangeExt()
14760 -> ExecuteCustomElementAction()
14762 -> (infinitely repeating the above sequence of function calls)
14763 which occurs when killing the player while having a CE with the setting
14764 "kill player X when explosion of <player X>"; the solution using a new
14765 field "player->killed" was chosen for backwards compatibility, although
14766 clever use of the fields "player->active" etc. would probably also work */
14768 if (player->killed)
14772 player->killed = TRUE;
14774 /* remove accessible field at the player's position */
14775 Feld[jx][jy] = EL_EMPTY;
14777 /* deactivate shield (else Bang()/Explode() would not work right) */
14778 player->shield_normal_time_left = 0;
14779 player->shield_deadly_time_left = 0;
14782 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14783 player->killed, player->active, player->reanimated);
14789 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14790 player->killed, player->active, player->reanimated);
14793 #if USE_PLAYER_REANIMATION
14795 if (player->reanimated) /* killed player may have been reanimated */
14796 player->killed = player->reanimated = FALSE;
14798 BuryPlayer(player);
14800 if (player->killed) /* player may have been reanimated */
14801 BuryPlayer(player);
14804 BuryPlayer(player);
14808 static void KillPlayerUnlessEnemyProtected(int x, int y)
14810 if (!PLAYER_ENEMY_PROTECTED(x, y))
14811 KillPlayer(PLAYERINFO(x, y));
14814 static void KillPlayerUnlessExplosionProtected(int x, int y)
14816 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14817 KillPlayer(PLAYERINFO(x, y));
14820 void BuryPlayer(struct PlayerInfo *player)
14822 int jx = player->jx, jy = player->jy;
14824 if (!player->active)
14827 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14828 PlayLevelSound(jx, jy, SND_GAME_LOSING);
14830 player->GameOver = TRUE;
14831 RemovePlayer(player);
14834 void RemovePlayer(struct PlayerInfo *player)
14836 int jx = player->jx, jy = player->jy;
14837 int i, found = FALSE;
14839 player->present = FALSE;
14840 player->active = FALSE;
14842 if (!ExplodeField[jx][jy])
14843 StorePlayer[jx][jy] = 0;
14845 if (player->is_moving)
14846 TEST_DrawLevelField(player->last_jx, player->last_jy);
14848 for (i = 0; i < MAX_PLAYERS; i++)
14849 if (stored_player[i].active)
14853 AllPlayersGone = TRUE;
14859 #if USE_NEW_SNAP_DELAY
14860 static void setFieldForSnapping(int x, int y, int element, int direction)
14862 struct ElementInfo *ei = &element_info[element];
14863 int direction_bit = MV_DIR_TO_BIT(direction);
14864 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14865 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14866 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14868 Feld[x][y] = EL_ELEMENT_SNAPPING;
14869 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14871 ResetGfxAnimation(x, y);
14873 GfxElement[x][y] = element;
14874 GfxAction[x][y] = action;
14875 GfxDir[x][y] = direction;
14876 GfxFrame[x][y] = -1;
14881 =============================================================================
14882 checkDiagonalPushing()
14883 -----------------------------------------------------------------------------
14884 check if diagonal input device direction results in pushing of object
14885 (by checking if the alternative direction is walkable, diggable, ...)
14886 =============================================================================
14889 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14890 int x, int y, int real_dx, int real_dy)
14892 int jx, jy, dx, dy, xx, yy;
14894 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
14897 /* diagonal direction: check alternative direction */
14902 xx = jx + (dx == 0 ? real_dx : 0);
14903 yy = jy + (dy == 0 ? real_dy : 0);
14905 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14909 =============================================================================
14911 -----------------------------------------------------------------------------
14912 x, y: field next to player (non-diagonal) to try to dig to
14913 real_dx, real_dy: direction as read from input device (can be diagonal)
14914 =============================================================================
14917 static int DigField(struct PlayerInfo *player,
14918 int oldx, int oldy, int x, int y,
14919 int real_dx, int real_dy, int mode)
14921 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14922 boolean player_was_pushing = player->is_pushing;
14923 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14924 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14925 int jx = oldx, jy = oldy;
14926 int dx = x - jx, dy = y - jy;
14927 int nextx = x + dx, nexty = y + dy;
14928 int move_direction = (dx == -1 ? MV_LEFT :
14929 dx == +1 ? MV_RIGHT :
14931 dy == +1 ? MV_DOWN : MV_NONE);
14932 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14933 int dig_side = MV_DIR_OPPOSITE(move_direction);
14934 int old_element = Feld[jx][jy];
14935 #if USE_FIXED_DONT_RUN_INTO
14936 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14942 if (is_player) /* function can also be called by EL_PENGUIN */
14944 if (player->MovPos == 0)
14946 player->is_digging = FALSE;
14947 player->is_collecting = FALSE;
14950 if (player->MovPos == 0) /* last pushing move finished */
14951 player->is_pushing = FALSE;
14953 if (mode == DF_NO_PUSH) /* player just stopped pushing */
14955 player->is_switching = FALSE;
14956 player->push_delay = -1;
14958 return MP_NO_ACTION;
14962 #if !USE_FIXED_DONT_RUN_INTO
14963 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14964 return MP_NO_ACTION;
14967 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14968 old_element = Back[jx][jy];
14970 /* in case of element dropped at player position, check background */
14971 else if (Back[jx][jy] != EL_EMPTY &&
14972 game.engine_version >= VERSION_IDENT(2,2,0,0))
14973 old_element = Back[jx][jy];
14975 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14976 return MP_NO_ACTION; /* field has no opening in this direction */
14978 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14979 return MP_NO_ACTION; /* field has no opening in this direction */
14981 #if USE_FIXED_DONT_RUN_INTO
14982 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14986 Feld[jx][jy] = player->artwork_element;
14987 InitMovingField(jx, jy, MV_DOWN);
14988 Store[jx][jy] = EL_ACID;
14989 ContinueMoving(jx, jy);
14990 BuryPlayer(player);
14992 return MP_DONT_RUN_INTO;
14996 #if USE_FIXED_DONT_RUN_INTO
14997 if (player_can_move && DONT_RUN_INTO(element))
14999 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
15001 return MP_DONT_RUN_INTO;
15005 #if USE_FIXED_DONT_RUN_INTO
15006 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
15007 return MP_NO_ACTION;
15010 #if !USE_FIXED_DONT_RUN_INTO
15011 element = Feld[x][y];
15014 collect_count = element_info[element].collect_count_initial;
15016 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
15017 return MP_NO_ACTION;
15019 if (game.engine_version < VERSION_IDENT(2,2,0,0))
15020 player_can_move = player_can_move_or_snap;
15022 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
15023 game.engine_version >= VERSION_IDENT(2,2,0,0))
15025 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
15026 player->index_bit, dig_side);
15027 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15028 player->index_bit, dig_side);
15030 if (element == EL_DC_LANDMINE)
15033 if (Feld[x][y] != element) /* field changed by snapping */
15036 return MP_NO_ACTION;
15039 #if USE_PLAYER_GRAVITY
15040 if (player->gravity && is_player && !player->is_auto_moving &&
15041 canFallDown(player) && move_direction != MV_DOWN &&
15042 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15043 return MP_NO_ACTION; /* player cannot walk here due to gravity */
15045 if (game.gravity && is_player && !player->is_auto_moving &&
15046 canFallDown(player) && move_direction != MV_DOWN &&
15047 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15048 return MP_NO_ACTION; /* player cannot walk here due to gravity */
15051 if (player_can_move &&
15052 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
15054 int sound_element = SND_ELEMENT(element);
15055 int sound_action = ACTION_WALKING;
15057 if (IS_RND_GATE(element))
15059 if (!player->key[RND_GATE_NR(element)])
15060 return MP_NO_ACTION;
15062 else if (IS_RND_GATE_GRAY(element))
15064 if (!player->key[RND_GATE_GRAY_NR(element)])
15065 return MP_NO_ACTION;
15067 else if (IS_RND_GATE_GRAY_ACTIVE(element))
15069 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
15070 return MP_NO_ACTION;
15072 else if (element == EL_EXIT_OPEN ||
15073 element == EL_EM_EXIT_OPEN ||
15075 element == EL_EM_EXIT_OPENING ||
15077 element == EL_STEEL_EXIT_OPEN ||
15078 element == EL_EM_STEEL_EXIT_OPEN ||
15080 element == EL_EM_STEEL_EXIT_OPENING ||
15082 element == EL_SP_EXIT_OPEN ||
15083 element == EL_SP_EXIT_OPENING)
15085 sound_action = ACTION_PASSING; /* player is passing exit */
15087 else if (element == EL_EMPTY)
15089 sound_action = ACTION_MOVING; /* nothing to walk on */
15092 /* play sound from background or player, whatever is available */
15093 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
15094 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
15096 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
15098 else if (player_can_move &&
15099 IS_PASSABLE(element) && canPassField(x, y, move_direction))
15101 if (!ACCESS_FROM(element, opposite_direction))
15102 return MP_NO_ACTION; /* field not accessible from this direction */
15104 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
15105 return MP_NO_ACTION;
15107 if (IS_EM_GATE(element))
15109 if (!player->key[EM_GATE_NR(element)])
15110 return MP_NO_ACTION;
15112 else if (IS_EM_GATE_GRAY(element))
15114 if (!player->key[EM_GATE_GRAY_NR(element)])
15115 return MP_NO_ACTION;
15117 else if (IS_EM_GATE_GRAY_ACTIVE(element))
15119 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
15120 return MP_NO_ACTION;
15122 else if (IS_EMC_GATE(element))
15124 if (!player->key[EMC_GATE_NR(element)])
15125 return MP_NO_ACTION;
15127 else if (IS_EMC_GATE_GRAY(element))
15129 if (!player->key[EMC_GATE_GRAY_NR(element)])
15130 return MP_NO_ACTION;
15132 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
15134 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
15135 return MP_NO_ACTION;
15137 else if (element == EL_DC_GATE_WHITE ||
15138 element == EL_DC_GATE_WHITE_GRAY ||
15139 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15141 if (player->num_white_keys == 0)
15142 return MP_NO_ACTION;
15144 player->num_white_keys--;
15146 else if (IS_SP_PORT(element))
15148 if (element == EL_SP_GRAVITY_PORT_LEFT ||
15149 element == EL_SP_GRAVITY_PORT_RIGHT ||
15150 element == EL_SP_GRAVITY_PORT_UP ||
15151 element == EL_SP_GRAVITY_PORT_DOWN)
15152 #if USE_PLAYER_GRAVITY
15153 player->gravity = !player->gravity;
15155 game.gravity = !game.gravity;
15157 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15158 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15159 element == EL_SP_GRAVITY_ON_PORT_UP ||
15160 element == EL_SP_GRAVITY_ON_PORT_DOWN)
15161 #if USE_PLAYER_GRAVITY
15162 player->gravity = TRUE;
15164 game.gravity = TRUE;
15166 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15167 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15168 element == EL_SP_GRAVITY_OFF_PORT_UP ||
15169 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15170 #if USE_PLAYER_GRAVITY
15171 player->gravity = FALSE;
15173 game.gravity = FALSE;
15177 /* automatically move to the next field with double speed */
15178 player->programmed_action = move_direction;
15180 if (player->move_delay_reset_counter == 0)
15182 player->move_delay_reset_counter = 2; /* two double speed steps */
15184 DOUBLE_PLAYER_SPEED(player);
15187 PlayLevelSoundAction(x, y, ACTION_PASSING);
15189 else if (player_can_move_or_snap && IS_DIGGABLE(element))
15193 if (mode != DF_SNAP)
15195 GfxElement[x][y] = GFX_ELEMENT(element);
15196 player->is_digging = TRUE;
15199 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15201 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15202 player->index_bit, dig_side);
15204 if (mode == DF_SNAP)
15206 #if USE_NEW_SNAP_DELAY
15207 if (level.block_snap_field)
15208 setFieldForSnapping(x, y, element, move_direction);
15210 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15212 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15215 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15216 player->index_bit, dig_side);
15219 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15223 if (is_player && mode != DF_SNAP)
15225 GfxElement[x][y] = element;
15226 player->is_collecting = TRUE;
15229 if (element == EL_SPEED_PILL)
15231 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15233 else if (element == EL_EXTRA_TIME && level.time > 0)
15235 TimeLeft += level.extra_time;
15238 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15240 DisplayGameControlValues();
15242 DrawGameValue_Time(TimeLeft);
15245 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15247 player->shield_normal_time_left += level.shield_normal_time;
15248 if (element == EL_SHIELD_DEADLY)
15249 player->shield_deadly_time_left += level.shield_deadly_time;
15251 else if (element == EL_DYNAMITE ||
15252 element == EL_EM_DYNAMITE ||
15253 element == EL_SP_DISK_RED)
15255 if (player->inventory_size < MAX_INVENTORY_SIZE)
15256 player->inventory_element[player->inventory_size++] = element;
15258 DrawGameDoorValues();
15260 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15262 player->dynabomb_count++;
15263 player->dynabombs_left++;
15265 else if (element == EL_DYNABOMB_INCREASE_SIZE)
15267 player->dynabomb_size++;
15269 else if (element == EL_DYNABOMB_INCREASE_POWER)
15271 player->dynabomb_xl = TRUE;
15273 else if (IS_KEY(element))
15275 player->key[KEY_NR(element)] = TRUE;
15277 DrawGameDoorValues();
15279 else if (element == EL_DC_KEY_WHITE)
15281 player->num_white_keys++;
15283 /* display white keys? */
15284 /* DrawGameDoorValues(); */
15286 else if (IS_ENVELOPE(element))
15288 player->show_envelope = element;
15290 else if (element == EL_EMC_LENSES)
15292 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15294 RedrawAllInvisibleElementsForLenses();
15296 else if (element == EL_EMC_MAGNIFIER)
15298 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15300 RedrawAllInvisibleElementsForMagnifier();
15302 else if (IS_DROPPABLE(element) ||
15303 IS_THROWABLE(element)) /* can be collected and dropped */
15307 if (collect_count == 0)
15308 player->inventory_infinite_element = element;
15310 for (i = 0; i < collect_count; i++)
15311 if (player->inventory_size < MAX_INVENTORY_SIZE)
15312 player->inventory_element[player->inventory_size++] = element;
15314 DrawGameDoorValues();
15316 else if (collect_count > 0)
15318 local_player->gems_still_needed -= collect_count;
15319 if (local_player->gems_still_needed < 0)
15320 local_player->gems_still_needed = 0;
15323 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15325 DisplayGameControlValues();
15327 DrawGameValue_Emeralds(local_player->gems_still_needed);
15331 RaiseScoreElement(element);
15332 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15335 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15336 player->index_bit, dig_side);
15338 if (mode == DF_SNAP)
15340 #if USE_NEW_SNAP_DELAY
15341 if (level.block_snap_field)
15342 setFieldForSnapping(x, y, element, move_direction);
15344 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15346 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15349 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15350 player->index_bit, dig_side);
15353 else if (player_can_move_or_snap && IS_PUSHABLE(element))
15355 if (mode == DF_SNAP && element != EL_BD_ROCK)
15356 return MP_NO_ACTION;
15358 if (CAN_FALL(element) && dy)
15359 return MP_NO_ACTION;
15361 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15362 !(element == EL_SPRING && level.use_spring_bug))
15363 return MP_NO_ACTION;
15365 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15366 ((move_direction & MV_VERTICAL &&
15367 ((element_info[element].move_pattern & MV_LEFT &&
15368 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15369 (element_info[element].move_pattern & MV_RIGHT &&
15370 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15371 (move_direction & MV_HORIZONTAL &&
15372 ((element_info[element].move_pattern & MV_UP &&
15373 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15374 (element_info[element].move_pattern & MV_DOWN &&
15375 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15376 return MP_NO_ACTION;
15378 /* do not push elements already moving away faster than player */
15379 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15380 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15381 return MP_NO_ACTION;
15383 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15385 if (player->push_delay_value == -1 || !player_was_pushing)
15386 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15388 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15390 if (player->push_delay_value == -1)
15391 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15393 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15395 if (!player->is_pushing)
15396 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15399 player->is_pushing = TRUE;
15400 player->is_active = TRUE;
15402 if (!(IN_LEV_FIELD(nextx, nexty) &&
15403 (IS_FREE(nextx, nexty) ||
15404 (IS_SB_ELEMENT(element) &&
15405 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15406 (IS_CUSTOM_ELEMENT(element) &&
15407 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15408 return MP_NO_ACTION;
15410 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15411 return MP_NO_ACTION;
15413 if (player->push_delay == -1) /* new pushing; restart delay */
15414 player->push_delay = 0;
15416 if (player->push_delay < player->push_delay_value &&
15417 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15418 element != EL_SPRING && element != EL_BALLOON)
15420 /* make sure that there is no move delay before next try to push */
15421 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15422 player->move_delay = 0;
15424 return MP_NO_ACTION;
15427 if (IS_CUSTOM_ELEMENT(element) &&
15428 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15430 if (!DigFieldByCE(nextx, nexty, element))
15431 return MP_NO_ACTION;
15434 if (IS_SB_ELEMENT(element))
15436 if (element == EL_SOKOBAN_FIELD_FULL)
15438 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15439 local_player->sokobanfields_still_needed++;
15442 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15444 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15445 local_player->sokobanfields_still_needed--;
15448 Feld[x][y] = EL_SOKOBAN_OBJECT;
15450 if (Back[x][y] == Back[nextx][nexty])
15451 PlayLevelSoundAction(x, y, ACTION_PUSHING);
15452 else if (Back[x][y] != 0)
15453 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15456 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15460 if (local_player->sokobanfields_still_needed == 0 &&
15461 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
15463 if (local_player->sokobanfields_still_needed == 0 &&
15464 game.emulation == EMU_SOKOBAN)
15467 PlayerWins(player);
15469 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15473 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15475 InitMovingField(x, y, move_direction);
15476 GfxAction[x][y] = ACTION_PUSHING;
15478 if (mode == DF_SNAP)
15479 ContinueMoving(x, y);
15481 MovPos[x][y] = (dx != 0 ? dx : dy);
15483 Pushed[x][y] = TRUE;
15484 Pushed[nextx][nexty] = TRUE;
15486 if (game.engine_version < VERSION_IDENT(2,2,0,7))
15487 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15489 player->push_delay_value = -1; /* get new value later */
15491 /* check for element change _after_ element has been pushed */
15492 if (game.use_change_when_pushing_bug)
15494 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15495 player->index_bit, dig_side);
15496 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15497 player->index_bit, dig_side);
15500 else if (IS_SWITCHABLE(element))
15502 if (PLAYER_SWITCHING(player, x, y))
15504 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15505 player->index_bit, dig_side);
15510 player->is_switching = TRUE;
15511 player->switch_x = x;
15512 player->switch_y = y;
15514 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15516 if (element == EL_ROBOT_WHEEL)
15518 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15522 game.robot_wheel_active = TRUE;
15524 TEST_DrawLevelField(x, y);
15526 else if (element == EL_SP_TERMINAL)
15530 SCAN_PLAYFIELD(xx, yy)
15532 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15534 else if (Feld[xx][yy] == EL_SP_TERMINAL)
15535 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15538 else if (IS_BELT_SWITCH(element))
15540 ToggleBeltSwitch(x, y);
15542 else if (element == EL_SWITCHGATE_SWITCH_UP ||
15543 element == EL_SWITCHGATE_SWITCH_DOWN ||
15544 element == EL_DC_SWITCHGATE_SWITCH_UP ||
15545 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15547 ToggleSwitchgateSwitch(x, y);
15549 else if (element == EL_LIGHT_SWITCH ||
15550 element == EL_LIGHT_SWITCH_ACTIVE)
15552 ToggleLightSwitch(x, y);
15554 else if (element == EL_TIMEGATE_SWITCH ||
15555 element == EL_DC_TIMEGATE_SWITCH)
15557 ActivateTimegateSwitch(x, y);
15559 else if (element == EL_BALLOON_SWITCH_LEFT ||
15560 element == EL_BALLOON_SWITCH_RIGHT ||
15561 element == EL_BALLOON_SWITCH_UP ||
15562 element == EL_BALLOON_SWITCH_DOWN ||
15563 element == EL_BALLOON_SWITCH_NONE ||
15564 element == EL_BALLOON_SWITCH_ANY)
15566 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
15567 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15568 element == EL_BALLOON_SWITCH_UP ? MV_UP :
15569 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
15570 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
15573 else if (element == EL_LAMP)
15575 Feld[x][y] = EL_LAMP_ACTIVE;
15576 local_player->lights_still_needed--;
15578 ResetGfxAnimation(x, y);
15579 TEST_DrawLevelField(x, y);
15581 else if (element == EL_TIME_ORB_FULL)
15583 Feld[x][y] = EL_TIME_ORB_EMPTY;
15585 if (level.time > 0 || level.use_time_orb_bug)
15587 TimeLeft += level.time_orb_time;
15590 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15592 DisplayGameControlValues();
15594 DrawGameValue_Time(TimeLeft);
15598 ResetGfxAnimation(x, y);
15599 TEST_DrawLevelField(x, y);
15601 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15602 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15606 game.ball_state = !game.ball_state;
15608 SCAN_PLAYFIELD(xx, yy)
15610 int e = Feld[xx][yy];
15612 if (game.ball_state)
15614 if (e == EL_EMC_MAGIC_BALL)
15615 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15616 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15617 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15621 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15622 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15623 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15624 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15629 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15630 player->index_bit, dig_side);
15632 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15633 player->index_bit, dig_side);
15635 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15636 player->index_bit, dig_side);
15642 if (!PLAYER_SWITCHING(player, x, y))
15644 player->is_switching = TRUE;
15645 player->switch_x = x;
15646 player->switch_y = y;
15648 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15649 player->index_bit, dig_side);
15650 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15651 player->index_bit, dig_side);
15653 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15654 player->index_bit, dig_side);
15655 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15656 player->index_bit, dig_side);
15659 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15660 player->index_bit, dig_side);
15661 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15662 player->index_bit, dig_side);
15664 return MP_NO_ACTION;
15667 player->push_delay = -1;
15669 if (is_player) /* function can also be called by EL_PENGUIN */
15671 if (Feld[x][y] != element) /* really digged/collected something */
15673 player->is_collecting = !player->is_digging;
15674 player->is_active = TRUE;
15681 static boolean DigFieldByCE(int x, int y, int digging_element)
15683 int element = Feld[x][y];
15685 if (!IS_FREE(x, y))
15687 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15688 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15691 /* no element can dig solid indestructible elements */
15692 if (IS_INDESTRUCTIBLE(element) &&
15693 !IS_DIGGABLE(element) &&
15694 !IS_COLLECTIBLE(element))
15697 if (AmoebaNr[x][y] &&
15698 (element == EL_AMOEBA_FULL ||
15699 element == EL_BD_AMOEBA ||
15700 element == EL_AMOEBA_GROWING))
15702 AmoebaCnt[AmoebaNr[x][y]]--;
15703 AmoebaCnt2[AmoebaNr[x][y]]--;
15706 if (IS_MOVING(x, y))
15707 RemoveMovingField(x, y);
15711 TEST_DrawLevelField(x, y);
15714 /* if digged element was about to explode, prevent the explosion */
15715 ExplodeField[x][y] = EX_TYPE_NONE;
15717 PlayLevelSoundAction(x, y, action);
15720 Store[x][y] = EL_EMPTY;
15723 /* this makes it possible to leave the removed element again */
15724 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15725 Store[x][y] = element;
15727 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15729 int move_leave_element = element_info[digging_element].move_leave_element;
15731 /* this makes it possible to leave the removed element again */
15732 Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15733 element : move_leave_element);
15740 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15742 int jx = player->jx, jy = player->jy;
15743 int x = jx + dx, y = jy + dy;
15744 int snap_direction = (dx == -1 ? MV_LEFT :
15745 dx == +1 ? MV_RIGHT :
15747 dy == +1 ? MV_DOWN : MV_NONE);
15748 boolean can_continue_snapping = (level.continuous_snapping &&
15749 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15751 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15754 if (!player->active || !IN_LEV_FIELD(x, y))
15762 if (player->MovPos == 0)
15763 player->is_pushing = FALSE;
15765 player->is_snapping = FALSE;
15767 if (player->MovPos == 0)
15769 player->is_moving = FALSE;
15770 player->is_digging = FALSE;
15771 player->is_collecting = FALSE;
15777 #if USE_NEW_CONTINUOUS_SNAPPING
15778 /* prevent snapping with already pressed snap key when not allowed */
15779 if (player->is_snapping && !can_continue_snapping)
15782 if (player->is_snapping)
15786 player->MovDir = snap_direction;
15788 if (player->MovPos == 0)
15790 player->is_moving = FALSE;
15791 player->is_digging = FALSE;
15792 player->is_collecting = FALSE;
15795 player->is_dropping = FALSE;
15796 player->is_dropping_pressed = FALSE;
15797 player->drop_pressed_delay = 0;
15799 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15802 player->is_snapping = TRUE;
15803 player->is_active = TRUE;
15805 if (player->MovPos == 0)
15807 player->is_moving = FALSE;
15808 player->is_digging = FALSE;
15809 player->is_collecting = FALSE;
15812 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
15813 TEST_DrawLevelField(player->last_jx, player->last_jy);
15815 TEST_DrawLevelField(x, y);
15820 static boolean DropElement(struct PlayerInfo *player)
15822 int old_element, new_element;
15823 int dropx = player->jx, dropy = player->jy;
15824 int drop_direction = player->MovDir;
15825 int drop_side = drop_direction;
15827 int drop_element = get_next_dropped_element(player);
15829 int drop_element = (player->inventory_size > 0 ?
15830 player->inventory_element[player->inventory_size - 1] :
15831 player->inventory_infinite_element != EL_UNDEFINED ?
15832 player->inventory_infinite_element :
15833 player->dynabombs_left > 0 ?
15834 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15838 player->is_dropping_pressed = TRUE;
15840 /* do not drop an element on top of another element; when holding drop key
15841 pressed without moving, dropped element must move away before the next
15842 element can be dropped (this is especially important if the next element
15843 is dynamite, which can be placed on background for historical reasons) */
15844 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15847 if (IS_THROWABLE(drop_element))
15849 dropx += GET_DX_FROM_DIR(drop_direction);
15850 dropy += GET_DY_FROM_DIR(drop_direction);
15852 if (!IN_LEV_FIELD(dropx, dropy))
15856 old_element = Feld[dropx][dropy]; /* old element at dropping position */
15857 new_element = drop_element; /* default: no change when dropping */
15859 /* check if player is active, not moving and ready to drop */
15860 if (!player->active || player->MovPos || player->drop_delay > 0)
15863 /* check if player has anything that can be dropped */
15864 if (new_element == EL_UNDEFINED)
15867 /* check if drop key was pressed long enough for EM style dynamite */
15868 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15871 /* check if anything can be dropped at the current position */
15872 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15875 /* collected custom elements can only be dropped on empty fields */
15876 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15879 if (old_element != EL_EMPTY)
15880 Back[dropx][dropy] = old_element; /* store old element on this field */
15882 ResetGfxAnimation(dropx, dropy);
15883 ResetRandomAnimationValue(dropx, dropy);
15885 if (player->inventory_size > 0 ||
15886 player->inventory_infinite_element != EL_UNDEFINED)
15888 if (player->inventory_size > 0)
15890 player->inventory_size--;
15892 DrawGameDoorValues();
15894 if (new_element == EL_DYNAMITE)
15895 new_element = EL_DYNAMITE_ACTIVE;
15896 else if (new_element == EL_EM_DYNAMITE)
15897 new_element = EL_EM_DYNAMITE_ACTIVE;
15898 else if (new_element == EL_SP_DISK_RED)
15899 new_element = EL_SP_DISK_RED_ACTIVE;
15902 Feld[dropx][dropy] = new_element;
15904 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15905 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15906 el2img(Feld[dropx][dropy]), 0);
15908 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15910 /* needed if previous element just changed to "empty" in the last frame */
15911 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15913 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15914 player->index_bit, drop_side);
15915 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15917 player->index_bit, drop_side);
15919 TestIfElementTouchesCustomElement(dropx, dropy);
15921 else /* player is dropping a dyna bomb */
15923 player->dynabombs_left--;
15925 Feld[dropx][dropy] = new_element;
15927 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15928 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15929 el2img(Feld[dropx][dropy]), 0);
15931 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15934 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15935 InitField_WithBug1(dropx, dropy, FALSE);
15937 new_element = Feld[dropx][dropy]; /* element might have changed */
15939 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15940 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15942 int move_direction, nextx, nexty;
15944 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15945 MovDir[dropx][dropy] = drop_direction;
15947 move_direction = MovDir[dropx][dropy];
15948 nextx = dropx + GET_DX_FROM_DIR(move_direction);
15949 nexty = dropy + GET_DY_FROM_DIR(move_direction);
15951 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15953 #if USE_FIX_IMPACT_COLLISION
15954 /* do not cause impact style collision by dropping elements that can fall */
15955 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15957 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15961 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15962 player->is_dropping = TRUE;
15964 player->drop_pressed_delay = 0;
15965 player->is_dropping_pressed = FALSE;
15967 player->drop_x = dropx;
15968 player->drop_y = dropy;
15973 /* ------------------------------------------------------------------------- */
15974 /* game sound playing functions */
15975 /* ------------------------------------------------------------------------- */
15977 static int *loop_sound_frame = NULL;
15978 static int *loop_sound_volume = NULL;
15980 void InitPlayLevelSound()
15982 int num_sounds = getSoundListSize();
15984 checked_free(loop_sound_frame);
15985 checked_free(loop_sound_volume);
15987 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
15988 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15991 static void PlayLevelSound(int x, int y, int nr)
15993 int sx = SCREENX(x), sy = SCREENY(y);
15994 int volume, stereo_position;
15995 int max_distance = 8;
15996 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15998 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15999 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
16002 if (!IN_LEV_FIELD(x, y) ||
16003 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
16004 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
16007 volume = SOUND_MAX_VOLUME;
16009 if (!IN_SCR_FIELD(sx, sy))
16011 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
16012 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
16014 volume -= volume * (dx > dy ? dx : dy) / max_distance;
16017 stereo_position = (SOUND_MAX_LEFT +
16018 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
16019 (SCR_FIELDX + 2 * max_distance));
16021 if (IS_LOOP_SOUND(nr))
16023 /* This assures that quieter loop sounds do not overwrite louder ones,
16024 while restarting sound volume comparison with each new game frame. */
16026 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
16029 loop_sound_volume[nr] = volume;
16030 loop_sound_frame[nr] = FrameCounter;
16033 PlaySoundExt(nr, volume, stereo_position, type);
16036 static void PlayLevelSoundNearest(int x, int y, int sound_action)
16038 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
16039 x > LEVELX(BX2) ? LEVELX(BX2) : x,
16040 y < LEVELY(BY1) ? LEVELY(BY1) :
16041 y > LEVELY(BY2) ? LEVELY(BY2) : y,
16045 static void PlayLevelSoundAction(int x, int y, int action)
16047 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
16050 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
16052 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16054 if (sound_effect != SND_UNDEFINED)
16055 PlayLevelSound(x, y, sound_effect);
16058 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
16061 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16063 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16064 PlayLevelSound(x, y, sound_effect);
16067 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
16069 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16071 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16072 PlayLevelSound(x, y, sound_effect);
16075 static void StopLevelSoundActionIfLoop(int x, int y, int action)
16077 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16079 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16080 StopSound(sound_effect);
16083 static void PlayLevelMusic()
16085 if (levelset.music[level_nr] != MUS_UNDEFINED)
16086 PlayMusic(levelset.music[level_nr]); /* from config file */
16088 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
16091 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
16093 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
16094 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
16095 int x = xx - 1 - offset;
16096 int y = yy - 1 - offset;
16101 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
16105 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16109 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16113 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16117 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16121 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16125 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16128 case SAMPLE_android_clone:
16129 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16132 case SAMPLE_android_move:
16133 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16136 case SAMPLE_spring:
16137 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16141 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16145 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16148 case SAMPLE_eater_eat:
16149 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16153 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16156 case SAMPLE_collect:
16157 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16160 case SAMPLE_diamond:
16161 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16164 case SAMPLE_squash:
16165 /* !!! CHECK THIS !!! */
16167 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16169 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16173 case SAMPLE_wonderfall:
16174 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16178 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16182 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16186 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16190 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16194 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16198 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16201 case SAMPLE_wonder:
16202 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16206 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16209 case SAMPLE_exit_open:
16210 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16213 case SAMPLE_exit_leave:
16214 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16217 case SAMPLE_dynamite:
16218 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16222 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16226 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16230 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16234 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16238 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16242 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16246 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16251 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
16253 int element = map_element_SP_to_RND(element_sp);
16254 int action = map_action_SP_to_RND(action_sp);
16255 int offset = (setup.sp_show_border_elements ? 0 : 1);
16256 int x = xx - offset;
16257 int y = yy - offset;
16260 printf("::: %d -> %d\n", element_sp, action_sp);
16263 PlayLevelSoundElementAction(x, y, element, action);
16267 void ChangeTime(int value)
16269 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
16273 /* EMC game engine uses value from time counter of RND game engine */
16274 level.native_em_level->lev->time = *time;
16276 DrawGameValue_Time(*time);
16279 void RaiseScore(int value)
16281 /* EMC game engine and RND game engine have separate score counters */
16282 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
16283 &level.native_em_level->lev->score : &local_player->score);
16287 DrawGameValue_Score(*score);
16291 void RaiseScore(int value)
16293 local_player->score += value;
16296 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16298 DisplayGameControlValues();
16300 DrawGameValue_Score(local_player->score);
16304 void RaiseScoreElement(int element)
16309 case EL_BD_DIAMOND:
16310 case EL_EMERALD_YELLOW:
16311 case EL_EMERALD_RED:
16312 case EL_EMERALD_PURPLE:
16313 case EL_SP_INFOTRON:
16314 RaiseScore(level.score[SC_EMERALD]);
16317 RaiseScore(level.score[SC_DIAMOND]);
16320 RaiseScore(level.score[SC_CRYSTAL]);
16323 RaiseScore(level.score[SC_PEARL]);
16326 case EL_BD_BUTTERFLY:
16327 case EL_SP_ELECTRON:
16328 RaiseScore(level.score[SC_BUG]);
16331 case EL_BD_FIREFLY:
16332 case EL_SP_SNIKSNAK:
16333 RaiseScore(level.score[SC_SPACESHIP]);
16336 case EL_DARK_YAMYAM:
16337 RaiseScore(level.score[SC_YAMYAM]);
16340 RaiseScore(level.score[SC_ROBOT]);
16343 RaiseScore(level.score[SC_PACMAN]);
16346 RaiseScore(level.score[SC_NUT]);
16349 case EL_EM_DYNAMITE:
16350 case EL_SP_DISK_RED:
16351 case EL_DYNABOMB_INCREASE_NUMBER:
16352 case EL_DYNABOMB_INCREASE_SIZE:
16353 case EL_DYNABOMB_INCREASE_POWER:
16354 RaiseScore(level.score[SC_DYNAMITE]);
16356 case EL_SHIELD_NORMAL:
16357 case EL_SHIELD_DEADLY:
16358 RaiseScore(level.score[SC_SHIELD]);
16360 case EL_EXTRA_TIME:
16361 RaiseScore(level.extra_time_score);
16375 case EL_DC_KEY_WHITE:
16376 RaiseScore(level.score[SC_KEY]);
16379 RaiseScore(element_info[element].collect_score);
16384 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16386 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16388 #if defined(NETWORK_AVALIABLE)
16389 if (options.network)
16390 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16399 FadeSkipNextFadeIn();
16401 fading = fading_none;
16405 OpenDoor(DOOR_CLOSE_1);
16408 game_status = GAME_MODE_MAIN;
16411 DrawAndFadeInMainMenu(REDRAW_FIELD);
16419 FadeOut(REDRAW_FIELD);
16422 game_status = GAME_MODE_MAIN;
16424 DrawAndFadeInMainMenu(REDRAW_FIELD);
16428 else /* continue playing the game */
16430 if (tape.playing && tape.deactivate_display)
16431 TapeDeactivateDisplayOff(TRUE);
16433 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16435 if (tape.playing && tape.deactivate_display)
16436 TapeDeactivateDisplayOn();
16440 void RequestQuitGame(boolean ask_if_really_quit)
16442 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16443 boolean skip_request = AllPlayersGone || quick_quit;
16445 RequestQuitGameExt(skip_request, quick_quit,
16446 "Do you really want to quit the game ?");
16450 /* ------------------------------------------------------------------------- */
16451 /* random generator functions */
16452 /* ------------------------------------------------------------------------- */
16454 unsigned int InitEngineRandom_RND(long seed)
16456 game.num_random_calls = 0;
16459 unsigned int rnd_seed = InitEngineRandom(seed);
16461 printf("::: START RND: %d\n", rnd_seed);
16466 return InitEngineRandom(seed);
16472 unsigned int RND(int max)
16476 game.num_random_calls++;
16478 return GetEngineRandom(max);
16485 /* ------------------------------------------------------------------------- */
16486 /* game engine snapshot handling functions */
16487 /* ------------------------------------------------------------------------- */
16489 struct EngineSnapshotInfo
16491 /* runtime values for custom element collect score */
16492 int collect_score[NUM_CUSTOM_ELEMENTS];
16494 /* runtime values for group element choice position */
16495 int choice_pos[NUM_GROUP_ELEMENTS];
16497 /* runtime values for belt position animations */
16498 int belt_graphic[4][NUM_BELT_PARTS];
16499 int belt_anim_mode[4][NUM_BELT_PARTS];
16502 static struct EngineSnapshotInfo engine_snapshot_rnd;
16503 static char *snapshot_level_identifier = NULL;
16504 static int snapshot_level_nr = -1;
16506 static void SaveEngineSnapshotValues_RND()
16508 static int belt_base_active_element[4] =
16510 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16511 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16512 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16513 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16517 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16519 int element = EL_CUSTOM_START + i;
16521 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16524 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16526 int element = EL_GROUP_START + i;
16528 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16531 for (i = 0; i < 4; i++)
16533 for (j = 0; j < NUM_BELT_PARTS; j++)
16535 int element = belt_base_active_element[i] + j;
16536 int graphic = el2img(element);
16537 int anim_mode = graphic_info[graphic].anim_mode;
16539 engine_snapshot_rnd.belt_graphic[i][j] = graphic;
16540 engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
16545 static void LoadEngineSnapshotValues_RND()
16547 unsigned long num_random_calls = game.num_random_calls;
16550 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16552 int element = EL_CUSTOM_START + i;
16554 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16557 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16559 int element = EL_GROUP_START + i;
16561 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16564 for (i = 0; i < 4; i++)
16566 for (j = 0; j < NUM_BELT_PARTS; j++)
16568 int graphic = engine_snapshot_rnd.belt_graphic[i][j];
16569 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
16571 graphic_info[graphic].anim_mode = anim_mode;
16575 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16577 InitRND(tape.random_seed);
16578 for (i = 0; i < num_random_calls; i++)
16582 if (game.num_random_calls != num_random_calls)
16584 Error(ERR_INFO, "number of random calls out of sync");
16585 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16586 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16587 Error(ERR_EXIT, "this should not happen -- please debug");
16591 void SaveEngineSnapshot()
16593 /* do not save snapshots from editor */
16594 if (level_editor_test_game)
16597 /* free previous snapshot buffers, if needed */
16598 FreeEngineSnapshotBuffers();
16600 /* copy some special values to a structure better suited for the snapshot */
16602 SaveEngineSnapshotValues_RND();
16603 SaveEngineSnapshotValues_EM();
16604 SaveEngineSnapshotValues_SP();
16606 /* save values stored in special snapshot structure */
16608 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16609 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16610 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16612 /* save further RND engine values */
16614 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16615 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16616 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16618 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16619 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16620 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16621 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16623 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16624 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16625 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16626 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16627 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16629 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16630 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16631 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16633 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16635 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16637 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16638 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16640 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16641 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16642 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16643 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16644 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16645 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16646 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16647 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16648 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16649 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16650 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16651 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16652 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16653 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16654 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16655 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16656 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16657 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16659 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16660 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16662 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16663 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16664 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16666 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16667 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16669 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16670 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16671 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16672 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16673 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16675 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16676 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16678 /* save level identification information */
16680 setString(&snapshot_level_identifier, leveldir_current->identifier);
16681 snapshot_level_nr = level_nr;
16684 ListNode *node = engine_snapshot_list_rnd;
16687 while (node != NULL)
16689 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16694 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16698 void LoadEngineSnapshot()
16700 /* restore generically stored snapshot buffers */
16702 LoadEngineSnapshotBuffers();
16704 /* restore special values from snapshot structure */
16706 LoadEngineSnapshotValues_RND();
16707 LoadEngineSnapshotValues_EM();
16708 LoadEngineSnapshotValues_SP();
16711 boolean CheckEngineSnapshot()
16713 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16714 snapshot_level_nr == level_nr);
16718 /* ---------- new game button stuff ---------------------------------------- */
16726 } gamebutton_info[NUM_GAME_BUTTONS] =
16729 IMG_GAME_BUTTON_GFX_STOP, &game.button.stop,
16730 GAME_CTRL_ID_STOP, "stop game"
16733 IMG_GAME_BUTTON_GFX_PAUSE, &game.button.pause,
16734 GAME_CTRL_ID_PAUSE, "pause game"
16737 IMG_GAME_BUTTON_GFX_PLAY, &game.button.play,
16738 GAME_CTRL_ID_PLAY, "play game"
16741 IMG_GAME_BUTTON_GFX_SOUND_MUSIC, &game.button.sound_music,
16742 SOUND_CTRL_ID_MUSIC, "background music on/off"
16745 IMG_GAME_BUTTON_GFX_SOUND_LOOPS, &game.button.sound_loops,
16746 SOUND_CTRL_ID_LOOPS, "sound loops on/off"
16749 IMG_GAME_BUTTON_GFX_SOUND_SIMPLE, &game.button.sound_simple,
16750 SOUND_CTRL_ID_SIMPLE, "normal sounds on/off"
16754 void CreateGameButtons()
16758 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16760 struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
16761 struct Rect *pos = gamebutton_info[i].pos;
16762 struct GadgetInfo *gi;
16765 unsigned long event_mask;
16766 int gd_x = gfx->src_x;
16767 int gd_y = gfx->src_y;
16768 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
16769 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
16770 int gd_xa = gfx->src_x + gfx->active_xoffset;
16771 int gd_ya = gfx->src_y + gfx->active_yoffset;
16772 int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16773 int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16776 if (id == GAME_CTRL_ID_STOP ||
16777 id == GAME_CTRL_ID_PAUSE ||
16778 id == GAME_CTRL_ID_PLAY)
16780 button_type = GD_TYPE_NORMAL_BUTTON;
16782 event_mask = GD_EVENT_RELEASED;
16786 button_type = GD_TYPE_CHECK_BUTTON;
16788 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16789 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16790 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16791 event_mask = GD_EVENT_PRESSED;
16794 gi = CreateGadget(GDI_CUSTOM_ID, id,
16795 GDI_INFO_TEXT, gamebutton_info[i].infotext,
16796 GDI_X, DX + pos->x,
16797 GDI_Y, DY + pos->y,
16798 GDI_WIDTH, gfx->width,
16799 GDI_HEIGHT, gfx->height,
16800 GDI_TYPE, button_type,
16801 GDI_STATE, GD_BUTTON_UNPRESSED,
16802 GDI_CHECKED, checked,
16803 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16804 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16805 GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16806 GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16807 GDI_DIRECT_DRAW, FALSE,
16808 GDI_EVENT_MASK, event_mask,
16809 GDI_CALLBACK_ACTION, HandleGameButtons,
16813 Error(ERR_EXIT, "cannot create gadget");
16815 game_gadget[id] = gi;
16819 void FreeGameButtons()
16823 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16824 FreeGadget(game_gadget[i]);
16827 static void MapGameButtons()
16831 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16832 MapGadget(game_gadget[i]);
16835 void UnmapGameButtons()
16839 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16840 UnmapGadget(game_gadget[i]);
16843 void RedrawGameButtons()
16847 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16848 RedrawGadget(game_gadget[i]);
16851 static void HandleGameButtonsExt(int id)
16853 if (game_status != GAME_MODE_PLAYING)
16858 case GAME_CTRL_ID_STOP:
16862 RequestQuitGame(TRUE);
16865 case GAME_CTRL_ID_PAUSE:
16866 if (options.network)
16868 #if defined(NETWORK_AVALIABLE)
16870 SendToServer_ContinuePlaying();
16872 SendToServer_PausePlaying();
16876 TapeTogglePause(TAPE_TOGGLE_MANUAL);
16879 case GAME_CTRL_ID_PLAY:
16882 #if defined(NETWORK_AVALIABLE)
16883 if (options.network)
16884 SendToServer_ContinuePlaying();
16888 tape.pausing = FALSE;
16889 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16894 case SOUND_CTRL_ID_MUSIC:
16895 if (setup.sound_music)
16897 setup.sound_music = FALSE;
16901 else if (audio.music_available)
16903 setup.sound = setup.sound_music = TRUE;
16905 SetAudioMode(setup.sound);
16911 case SOUND_CTRL_ID_LOOPS:
16912 if (setup.sound_loops)
16913 setup.sound_loops = FALSE;
16914 else if (audio.loops_available)
16916 setup.sound = setup.sound_loops = TRUE;
16918 SetAudioMode(setup.sound);
16922 case SOUND_CTRL_ID_SIMPLE:
16923 if (setup.sound_simple)
16924 setup.sound_simple = FALSE;
16925 else if (audio.sound_available)
16927 setup.sound = setup.sound_simple = TRUE;
16929 SetAudioMode(setup.sound);
16938 static void HandleGameButtons(struct GadgetInfo *gi)
16940 HandleGameButtonsExt(gi->custom_id);
16943 void HandleSoundButtonKeys(Key key)
16946 if (key == setup.shortcut.sound_simple)
16947 ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16948 else if (key == setup.shortcut.sound_loops)
16949 ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16950 else if (key == setup.shortcut.sound_music)
16951 ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16953 if (key == setup.shortcut.sound_simple)
16954 HandleGameButtonsExt(SOUND_CTRL_ID_SIMPLE);
16955 else if (key == setup.shortcut.sound_loops)
16956 HandleGameButtonsExt(SOUND_CTRL_ID_LOOPS);
16957 else if (key == setup.shortcut.sound_music)
16958 HandleGameButtonsExt(SOUND_CTRL_ID_MUSIC);