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;
4359 if (TILESIZE_VAR < TILESIZE && EVEN(SCR_FIELDX))
4367 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4368 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4370 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4371 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4373 /* if local player not found, look for custom element that might create
4374 the player (make some assumptions about the right custom element) */
4375 if (!local_player->present)
4377 int start_x = 0, start_y = 0;
4378 int found_rating = 0;
4379 int found_element = EL_UNDEFINED;
4380 int player_nr = local_player->index_nr;
4382 SCAN_PLAYFIELD(x, y)
4384 int element = Feld[x][y];
4389 if (level.use_start_element[player_nr] &&
4390 level.start_element[player_nr] == element &&
4397 found_element = element;
4400 if (!IS_CUSTOM_ELEMENT(element))
4403 if (CAN_CHANGE(element))
4405 for (i = 0; i < element_info[element].num_change_pages; i++)
4407 /* check for player created from custom element as single target */
4408 content = element_info[element].change_page[i].target_element;
4409 is_player = ELEM_IS_PLAYER(content);
4411 if (is_player && (found_rating < 3 ||
4412 (found_rating == 3 && element < found_element)))
4418 found_element = element;
4423 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4425 /* check for player created from custom element as explosion content */
4426 content = element_info[element].content.e[xx][yy];
4427 is_player = ELEM_IS_PLAYER(content);
4429 if (is_player && (found_rating < 2 ||
4430 (found_rating == 2 && element < found_element)))
4432 start_x = x + xx - 1;
4433 start_y = y + yy - 1;
4436 found_element = element;
4439 if (!CAN_CHANGE(element))
4442 for (i = 0; i < element_info[element].num_change_pages; i++)
4444 /* check for player created from custom element as extended target */
4446 element_info[element].change_page[i].target_content.e[xx][yy];
4448 is_player = ELEM_IS_PLAYER(content);
4450 if (is_player && (found_rating < 1 ||
4451 (found_rating == 1 && element < found_element)))
4453 start_x = x + xx - 1;
4454 start_y = y + yy - 1;
4457 found_element = element;
4463 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
4464 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4467 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4468 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4473 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
4474 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4475 local_player->jx - MIDPOSX);
4477 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4478 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4479 local_player->jy - MIDPOSY);
4483 /* do not use PLAYING mask for fading out from main screen */
4484 game_status = GAME_MODE_MAIN;
4489 if (!game.restart_level)
4490 CloseDoor(DOOR_CLOSE_1);
4493 if (level_editor_test_game)
4494 FadeSkipNextFadeIn();
4496 FadeSetEnterScreen();
4498 if (level_editor_test_game)
4499 fading = fading_none;
4501 fading = menu.destination;
4505 FadeOut(REDRAW_FIELD);
4508 FadeOut(REDRAW_FIELD);
4512 game_status = GAME_MODE_PLAYING;
4515 /* !!! FIX THIS (START) !!! */
4516 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4518 InitGameEngine_EM();
4520 /* blit playfield from scroll buffer to normal back buffer for fading in */
4521 BlitScreenToBitmap_EM(backbuffer);
4523 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4525 InitGameEngine_SP();
4527 /* blit playfield from scroll buffer to normal back buffer for fading in */
4528 BlitScreenToBitmap_SP(backbuffer);
4535 /* after drawing the level, correct some elements */
4536 if (game.timegate_time_left == 0)
4537 CloseAllOpenTimegates();
4539 /* blit playfield from scroll buffer to normal back buffer for fading in */
4540 if (setup.soft_scrolling)
4541 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4543 redraw_mask |= REDRAW_FROM_BACKBUFFER;
4545 /* !!! FIX THIS (END) !!! */
4548 FadeIn(REDRAW_FIELD);
4551 FadeIn(REDRAW_FIELD);
4556 if (!game.restart_level)
4558 /* copy default game door content to main double buffer */
4561 /* !!! CHECK AGAIN !!! */
4562 SetPanelBackground();
4563 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4564 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4566 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
4568 /* (ClearRectangle() only needed if panel bitmap is smaller than panel) */
4569 ClearRectangle(drawto, DX, DY, DXSIZE, DYSIZE);
4570 BlitBitmap(gfx->bitmap, drawto, gfx->src_x, gfx->src_y,
4571 MIN(gfx->width, DXSIZE), MIN(gfx->height, DYSIZE), DX, DY);
4574 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4575 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4579 SetPanelBackground();
4580 SetDrawBackgroundMask(REDRAW_DOOR_1);
4583 UpdateAndDisplayGameControlValues();
4585 UpdateGameDoorValues();
4586 DrawGameDoorValues();
4589 if (!game.restart_level)
4593 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4594 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4595 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4599 /* copy actual game door content to door double buffer for OpenDoor() */
4600 BlitBitmap(drawto, bitmap_db_door,
4601 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4603 OpenDoor(DOOR_OPEN_ALL);
4605 PlaySound(SND_GAME_STARTING);
4607 if (setup.sound_music)
4610 KeyboardAutoRepeatOffUnlessAutoplay();
4614 for (i = 0; i < MAX_PLAYERS; i++)
4615 printf("Player %d %sactive.\n",
4616 i + 1, (stored_player[i].active ? "" : "not "));
4627 game.restart_level = FALSE;
4630 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4632 /* this is used for non-R'n'D game engines to update certain engine values */
4634 /* needed to determine if sounds are played within the visible screen area */
4635 scroll_x = actual_scroll_x;
4636 scroll_y = actual_scroll_y;
4639 void InitMovDir(int x, int y)
4641 int i, element = Feld[x][y];
4642 static int xy[4][2] =
4649 static int direction[3][4] =
4651 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4652 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4653 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4662 Feld[x][y] = EL_BUG;
4663 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4666 case EL_SPACESHIP_RIGHT:
4667 case EL_SPACESHIP_UP:
4668 case EL_SPACESHIP_LEFT:
4669 case EL_SPACESHIP_DOWN:
4670 Feld[x][y] = EL_SPACESHIP;
4671 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4674 case EL_BD_BUTTERFLY_RIGHT:
4675 case EL_BD_BUTTERFLY_UP:
4676 case EL_BD_BUTTERFLY_LEFT:
4677 case EL_BD_BUTTERFLY_DOWN:
4678 Feld[x][y] = EL_BD_BUTTERFLY;
4679 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4682 case EL_BD_FIREFLY_RIGHT:
4683 case EL_BD_FIREFLY_UP:
4684 case EL_BD_FIREFLY_LEFT:
4685 case EL_BD_FIREFLY_DOWN:
4686 Feld[x][y] = EL_BD_FIREFLY;
4687 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4690 case EL_PACMAN_RIGHT:
4692 case EL_PACMAN_LEFT:
4693 case EL_PACMAN_DOWN:
4694 Feld[x][y] = EL_PACMAN;
4695 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4698 case EL_YAMYAM_LEFT:
4699 case EL_YAMYAM_RIGHT:
4701 case EL_YAMYAM_DOWN:
4702 Feld[x][y] = EL_YAMYAM;
4703 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4706 case EL_SP_SNIKSNAK:
4707 MovDir[x][y] = MV_UP;
4710 case EL_SP_ELECTRON:
4711 MovDir[x][y] = MV_LEFT;
4718 Feld[x][y] = EL_MOLE;
4719 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4723 if (IS_CUSTOM_ELEMENT(element))
4725 struct ElementInfo *ei = &element_info[element];
4726 int move_direction_initial = ei->move_direction_initial;
4727 int move_pattern = ei->move_pattern;
4729 if (move_direction_initial == MV_START_PREVIOUS)
4731 if (MovDir[x][y] != MV_NONE)
4734 move_direction_initial = MV_START_AUTOMATIC;
4737 if (move_direction_initial == MV_START_RANDOM)
4738 MovDir[x][y] = 1 << RND(4);
4739 else if (move_direction_initial & MV_ANY_DIRECTION)
4740 MovDir[x][y] = move_direction_initial;
4741 else if (move_pattern == MV_ALL_DIRECTIONS ||
4742 move_pattern == MV_TURNING_LEFT ||
4743 move_pattern == MV_TURNING_RIGHT ||
4744 move_pattern == MV_TURNING_LEFT_RIGHT ||
4745 move_pattern == MV_TURNING_RIGHT_LEFT ||
4746 move_pattern == MV_TURNING_RANDOM)
4747 MovDir[x][y] = 1 << RND(4);
4748 else if (move_pattern == MV_HORIZONTAL)
4749 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4750 else if (move_pattern == MV_VERTICAL)
4751 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4752 else if (move_pattern & MV_ANY_DIRECTION)
4753 MovDir[x][y] = element_info[element].move_pattern;
4754 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4755 move_pattern == MV_ALONG_RIGHT_SIDE)
4757 /* use random direction as default start direction */
4758 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4759 MovDir[x][y] = 1 << RND(4);
4761 for (i = 0; i < NUM_DIRECTIONS; i++)
4763 int x1 = x + xy[i][0];
4764 int y1 = y + xy[i][1];
4766 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4768 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4769 MovDir[x][y] = direction[0][i];
4771 MovDir[x][y] = direction[1][i];
4780 MovDir[x][y] = 1 << RND(4);
4782 if (element != EL_BUG &&
4783 element != EL_SPACESHIP &&
4784 element != EL_BD_BUTTERFLY &&
4785 element != EL_BD_FIREFLY)
4788 for (i = 0; i < NUM_DIRECTIONS; i++)
4790 int x1 = x + xy[i][0];
4791 int y1 = y + xy[i][1];
4793 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4795 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4797 MovDir[x][y] = direction[0][i];
4800 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4801 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4803 MovDir[x][y] = direction[1][i];
4812 GfxDir[x][y] = MovDir[x][y];
4815 void InitAmoebaNr(int x, int y)
4818 int group_nr = AmoebeNachbarNr(x, y);
4822 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4824 if (AmoebaCnt[i] == 0)
4832 AmoebaNr[x][y] = group_nr;
4833 AmoebaCnt[group_nr]++;
4834 AmoebaCnt2[group_nr]++;
4837 static void PlayerWins(struct PlayerInfo *player)
4839 player->LevelSolved = TRUE;
4840 player->GameOver = TRUE;
4842 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4843 level.native_em_level->lev->score : player->score);
4845 player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4846 player->LevelSolved_CountingScore = player->score_final;
4851 static int time, time_final;
4852 static int score, score_final;
4853 static int game_over_delay_1 = 0;
4854 static int game_over_delay_2 = 0;
4855 int game_over_delay_value_1 = 50;
4856 int game_over_delay_value_2 = 50;
4858 if (!local_player->LevelSolved_GameWon)
4862 /* do not start end game actions before the player stops moving (to exit) */
4863 if (local_player->MovPos)
4866 local_player->LevelSolved_GameWon = TRUE;
4867 local_player->LevelSolved_SaveTape = tape.recording;
4868 local_player->LevelSolved_SaveScore = !tape.playing;
4870 if (tape.auto_play) /* tape might already be stopped here */
4871 tape.auto_play_level_solved = TRUE;
4877 game_over_delay_1 = game_over_delay_value_1;
4878 game_over_delay_2 = game_over_delay_value_2;
4880 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4881 score = score_final = local_player->score_final;
4886 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4888 else if (level.time == 0 && TimePlayed < 999)
4891 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4894 local_player->score_final = score_final;
4896 if (level_editor_test_game)
4899 score = score_final;
4902 local_player->LevelSolved_CountingTime = time;
4903 local_player->LevelSolved_CountingScore = score;
4905 game_panel_controls[GAME_PANEL_TIME].value = time;
4906 game_panel_controls[GAME_PANEL_SCORE].value = score;
4908 DisplayGameControlValues();
4910 DrawGameValue_Time(time);
4911 DrawGameValue_Score(score);
4915 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4917 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4919 /* close exit door after last player */
4920 if ((AllPlayersGone &&
4921 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4922 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4923 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4924 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4925 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4927 int element = Feld[ExitX][ExitY];
4930 if (element == EL_EM_EXIT_OPEN ||
4931 element == EL_EM_STEEL_EXIT_OPEN)
4938 Feld[ExitX][ExitY] =
4939 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4940 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4941 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4942 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4943 EL_EM_STEEL_EXIT_CLOSING);
4945 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4949 /* player disappears */
4950 DrawLevelField(ExitX, ExitY);
4953 for (i = 0; i < MAX_PLAYERS; i++)
4955 struct PlayerInfo *player = &stored_player[i];
4957 if (player->present)
4959 RemovePlayer(player);
4961 /* player disappears */
4962 DrawLevelField(player->jx, player->jy);
4967 PlaySound(SND_GAME_WINNING);
4970 if (game_over_delay_1 > 0)
4972 game_over_delay_1--;
4977 if (time != time_final)
4979 int time_to_go = ABS(time_final - time);
4980 int time_count_dir = (time < time_final ? +1 : -1);
4981 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4983 time += time_count_steps * time_count_dir;
4984 score += time_count_steps * level.score[SC_TIME_BONUS];
4987 local_player->LevelSolved_CountingTime = time;
4988 local_player->LevelSolved_CountingScore = score;
4990 game_panel_controls[GAME_PANEL_TIME].value = time;
4991 game_panel_controls[GAME_PANEL_SCORE].value = score;
4993 DisplayGameControlValues();
4995 DrawGameValue_Time(time);
4996 DrawGameValue_Score(score);
4999 if (time == time_final)
5000 StopSound(SND_GAME_LEVELTIME_BONUS);
5001 else if (setup.sound_loops)
5002 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5004 PlaySound(SND_GAME_LEVELTIME_BONUS);
5009 local_player->LevelSolved_PanelOff = TRUE;
5011 if (game_over_delay_2 > 0)
5013 game_over_delay_2--;
5026 boolean raise_level = FALSE;
5028 local_player->LevelSolved_GameEnd = TRUE;
5030 CloseDoor(DOOR_CLOSE_1);
5032 if (local_player->LevelSolved_SaveTape)
5039 SaveTapeChecked(tape.level_nr); /* ask to save tape */
5041 SaveTape(tape.level_nr); /* ask to save tape */
5045 if (level_editor_test_game)
5047 game_status = GAME_MODE_MAIN;
5050 DrawAndFadeInMainMenu(REDRAW_FIELD);
5058 if (!local_player->LevelSolved_SaveScore)
5061 FadeOut(REDRAW_FIELD);
5064 game_status = GAME_MODE_MAIN;
5066 DrawAndFadeInMainMenu(REDRAW_FIELD);
5071 if (level_nr == leveldir_current->handicap_level)
5073 leveldir_current->handicap_level++;
5074 SaveLevelSetup_SeriesInfo();
5077 if (level_nr < leveldir_current->last_level)
5078 raise_level = TRUE; /* advance to next level */
5080 if ((hi_pos = NewHiScore()) >= 0)
5082 game_status = GAME_MODE_SCORES;
5084 DrawHallOfFame(hi_pos);
5095 FadeOut(REDRAW_FIELD);
5098 game_status = GAME_MODE_MAIN;
5106 DrawAndFadeInMainMenu(REDRAW_FIELD);
5115 LoadScore(level_nr);
5117 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5118 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
5121 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
5123 if (local_player->score_final > highscore[k].Score)
5125 /* player has made it to the hall of fame */
5127 if (k < MAX_SCORE_ENTRIES - 1)
5129 int m = MAX_SCORE_ENTRIES - 1;
5132 for (l = k; l < MAX_SCORE_ENTRIES; l++)
5133 if (strEqual(setup.player_name, highscore[l].Name))
5135 if (m == k) /* player's new highscore overwrites his old one */
5139 for (l = m; l > k; l--)
5141 strcpy(highscore[l].Name, highscore[l - 1].Name);
5142 highscore[l].Score = highscore[l - 1].Score;
5149 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5150 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5151 highscore[k].Score = local_player->score_final;
5157 else if (!strncmp(setup.player_name, highscore[k].Name,
5158 MAX_PLAYER_NAME_LEN))
5159 break; /* player already there with a higher score */
5165 SaveScore(level_nr);
5170 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
5172 int element = Feld[x][y];
5173 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5174 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5175 int horiz_move = (dx != 0);
5176 int sign = (horiz_move ? dx : dy);
5177 int step = sign * element_info[element].move_stepsize;
5179 /* special values for move stepsize for spring and things on conveyor belt */
5182 if (CAN_FALL(element) &&
5183 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5184 step = sign * MOVE_STEPSIZE_NORMAL / 2;
5185 else if (element == EL_SPRING)
5186 step = sign * MOVE_STEPSIZE_NORMAL * 2;
5192 inline static int getElementMoveStepsize(int x, int y)
5194 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5197 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5199 if (player->GfxAction != action || player->GfxDir != dir)
5202 printf("Player frame reset! (%d => %d, %d => %d)\n",
5203 player->GfxAction, action, player->GfxDir, dir);
5206 player->GfxAction = action;
5207 player->GfxDir = dir;
5209 player->StepFrame = 0;
5213 #if USE_GFX_RESET_GFX_ANIMATION
5214 static void ResetGfxFrame(int x, int y, boolean redraw)
5216 int element = Feld[x][y];
5217 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5218 int last_gfx_frame = GfxFrame[x][y];
5220 if (graphic_info[graphic].anim_global_sync)
5221 GfxFrame[x][y] = FrameCounter;
5222 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5223 GfxFrame[x][y] = CustomValue[x][y];
5224 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5225 GfxFrame[x][y] = element_info[element].collect_score;
5226 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5227 GfxFrame[x][y] = ChangeDelay[x][y];
5229 if (redraw && GfxFrame[x][y] != last_gfx_frame)
5230 DrawLevelGraphicAnimation(x, y, graphic);
5234 static void ResetGfxAnimation(int x, int y)
5236 GfxAction[x][y] = ACTION_DEFAULT;
5237 GfxDir[x][y] = MovDir[x][y];
5240 #if USE_GFX_RESET_GFX_ANIMATION
5241 ResetGfxFrame(x, y, FALSE);
5245 static void ResetRandomAnimationValue(int x, int y)
5247 GfxRandom[x][y] = INIT_GFX_RANDOM();
5250 void InitMovingField(int x, int y, int direction)
5252 int element = Feld[x][y];
5253 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5254 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5257 boolean is_moving_before, is_moving_after;
5259 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5262 /* check if element was/is moving or being moved before/after mode change */
5265 is_moving_before = (WasJustMoving[x][y] != 0);
5267 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5268 is_moving_before = WasJustMoving[x][y];
5271 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5273 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
5275 /* reset animation only for moving elements which change direction of moving
5276 or which just started or stopped moving
5277 (else CEs with property "can move" / "not moving" are reset each frame) */
5278 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5280 if (is_moving_before != is_moving_after ||
5281 direction != MovDir[x][y])
5282 ResetGfxAnimation(x, y);
5284 if ((is_moving_before || is_moving_after) && !continues_moving)
5285 ResetGfxAnimation(x, y);
5288 if (!continues_moving)
5289 ResetGfxAnimation(x, y);
5292 MovDir[x][y] = direction;
5293 GfxDir[x][y] = direction;
5295 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5296 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5297 direction == MV_DOWN && CAN_FALL(element) ?
5298 ACTION_FALLING : ACTION_MOVING);
5300 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5301 ACTION_FALLING : ACTION_MOVING);
5304 /* this is needed for CEs with property "can move" / "not moving" */
5306 if (is_moving_after)
5308 if (Feld[newx][newy] == EL_EMPTY)
5309 Feld[newx][newy] = EL_BLOCKED;
5311 MovDir[newx][newy] = MovDir[x][y];
5313 #if USE_NEW_CUSTOM_VALUE
5314 CustomValue[newx][newy] = CustomValue[x][y];
5317 GfxFrame[newx][newy] = GfxFrame[x][y];
5318 GfxRandom[newx][newy] = GfxRandom[x][y];
5319 GfxAction[newx][newy] = GfxAction[x][y];
5320 GfxDir[newx][newy] = GfxDir[x][y];
5324 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5326 int direction = MovDir[x][y];
5327 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5328 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
5334 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5336 int oldx = x, oldy = y;
5337 int direction = MovDir[x][y];
5339 if (direction == MV_LEFT)
5341 else if (direction == MV_RIGHT)
5343 else if (direction == MV_UP)
5345 else if (direction == MV_DOWN)
5348 *comes_from_x = oldx;
5349 *comes_from_y = oldy;
5352 int MovingOrBlocked2Element(int x, int y)
5354 int element = Feld[x][y];
5356 if (element == EL_BLOCKED)
5360 Blocked2Moving(x, y, &oldx, &oldy);
5361 return Feld[oldx][oldy];
5367 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5369 /* like MovingOrBlocked2Element(), but if element is moving
5370 and (x,y) is the field the moving element is just leaving,
5371 return EL_BLOCKED instead of the element value */
5372 int element = Feld[x][y];
5374 if (IS_MOVING(x, y))
5376 if (element == EL_BLOCKED)
5380 Blocked2Moving(x, y, &oldx, &oldy);
5381 return Feld[oldx][oldy];
5390 static void RemoveField(int x, int y)
5392 Feld[x][y] = EL_EMPTY;
5398 #if USE_NEW_CUSTOM_VALUE
5399 CustomValue[x][y] = 0;
5403 ChangeDelay[x][y] = 0;
5404 ChangePage[x][y] = -1;
5405 Pushed[x][y] = FALSE;
5408 ExplodeField[x][y] = EX_TYPE_NONE;
5411 GfxElement[x][y] = EL_UNDEFINED;
5412 GfxAction[x][y] = ACTION_DEFAULT;
5413 GfxDir[x][y] = MV_NONE;
5415 /* !!! this would prevent the removed tile from being redrawn !!! */
5416 GfxRedraw[x][y] = GFX_REDRAW_NONE;
5420 void RemoveMovingField(int x, int y)
5422 int oldx = x, oldy = y, newx = x, newy = y;
5423 int element = Feld[x][y];
5424 int next_element = EL_UNDEFINED;
5426 if (element != EL_BLOCKED && !IS_MOVING(x, y))
5429 if (IS_MOVING(x, y))
5431 Moving2Blocked(x, y, &newx, &newy);
5433 if (Feld[newx][newy] != EL_BLOCKED)
5435 /* element is moving, but target field is not free (blocked), but
5436 already occupied by something different (example: acid pool);
5437 in this case, only remove the moving field, but not the target */
5439 RemoveField(oldx, oldy);
5441 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5443 TEST_DrawLevelField(oldx, oldy);
5448 else if (element == EL_BLOCKED)
5450 Blocked2Moving(x, y, &oldx, &oldy);
5451 if (!IS_MOVING(oldx, oldy))
5455 if (element == EL_BLOCKED &&
5456 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5457 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5458 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5459 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5460 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5461 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5462 next_element = get_next_element(Feld[oldx][oldy]);
5464 RemoveField(oldx, oldy);
5465 RemoveField(newx, newy);
5467 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5469 if (next_element != EL_UNDEFINED)
5470 Feld[oldx][oldy] = next_element;
5472 TEST_DrawLevelField(oldx, oldy);
5473 TEST_DrawLevelField(newx, newy);
5476 void DrawDynamite(int x, int y)
5478 int sx = SCREENX(x), sy = SCREENY(y);
5479 int graphic = el2img(Feld[x][y]);
5482 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5485 if (IS_WALKABLE_INSIDE(Back[x][y]))
5489 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5490 else if (Store[x][y])
5491 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5493 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5495 if (Back[x][y] || Store[x][y])
5496 DrawGraphicThruMask(sx, sy, graphic, frame);
5498 DrawGraphic(sx, sy, graphic, frame);
5501 void CheckDynamite(int x, int y)
5503 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5507 if (MovDelay[x][y] != 0)
5510 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5516 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5521 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5523 boolean num_checked_players = 0;
5526 for (i = 0; i < MAX_PLAYERS; i++)
5528 if (stored_player[i].active)
5530 int sx = stored_player[i].jx;
5531 int sy = stored_player[i].jy;
5533 if (num_checked_players == 0)
5540 *sx1 = MIN(*sx1, sx);
5541 *sy1 = MIN(*sy1, sy);
5542 *sx2 = MAX(*sx2, sx);
5543 *sy2 = MAX(*sy2, sy);
5546 num_checked_players++;
5551 static boolean checkIfAllPlayersFitToScreen_RND()
5553 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5555 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5557 return (sx2 - sx1 < SCR_FIELDX &&
5558 sy2 - sy1 < SCR_FIELDY);
5561 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5563 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5565 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5567 *sx = (sx1 + sx2) / 2;
5568 *sy = (sy1 + sy2) / 2;
5571 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5572 boolean center_screen, boolean quick_relocation)
5574 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5575 boolean no_delay = (tape.warp_forward);
5576 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5577 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5579 if (quick_relocation)
5581 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5583 if (!level.shifted_relocation || center_screen)
5585 /* quick relocation (without scrolling), with centering of screen */
5587 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5588 x > SBX_Right + MIDPOSX ? SBX_Right :
5591 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5592 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5597 /* quick relocation (without scrolling), but do not center screen */
5599 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5600 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5603 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5604 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5607 int offset_x = x + (scroll_x - center_scroll_x);
5608 int offset_y = y + (scroll_y - center_scroll_y);
5610 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5611 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5612 offset_x - MIDPOSX);
5614 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5615 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5616 offset_y - MIDPOSY);
5622 if (!level.shifted_relocation || center_screen)
5624 /* quick relocation (without scrolling), with centering of screen */
5626 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5627 x > SBX_Right + MIDPOSX ? SBX_Right :
5630 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5631 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5636 /* quick relocation (without scrolling), but do not center screen */
5638 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5639 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5642 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5643 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5646 int offset_x = x + (scroll_x - center_scroll_x);
5647 int offset_y = y + (scroll_y - center_scroll_y);
5649 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5650 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5651 offset_x - MIDPOSX);
5653 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5654 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5655 offset_y - MIDPOSY);
5658 /* quick relocation (without scrolling), inside visible screen area */
5660 int offset = game.scroll_delay_value;
5662 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
5663 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5664 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5666 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
5667 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5668 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5670 /* don't scroll over playfield boundaries */
5671 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5672 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5674 /* don't scroll over playfield boundaries */
5675 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5676 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5680 RedrawPlayfield(TRUE, 0,0,0,0);
5685 int scroll_xx, scroll_yy;
5687 if (!level.shifted_relocation || center_screen)
5689 /* visible relocation (with scrolling), with centering of screen */
5691 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5692 x > SBX_Right + MIDPOSX ? SBX_Right :
5695 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5696 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5701 /* visible relocation (with scrolling), but do not center screen */
5703 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5704 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5707 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5708 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5711 int offset_x = x + (scroll_x - center_scroll_x);
5712 int offset_y = y + (scroll_y - center_scroll_y);
5714 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5715 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5716 offset_x - MIDPOSX);
5718 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5719 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5720 offset_y - MIDPOSY);
5725 /* visible relocation (with scrolling), with centering of screen */
5727 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5728 x > SBX_Right + MIDPOSX ? SBX_Right :
5731 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5732 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5736 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5738 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5741 int fx = FX, fy = FY;
5743 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5744 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5746 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5752 fx += dx * TILEX / 2;
5753 fy += dy * TILEY / 2;
5755 ScrollLevel(dx, dy);
5758 /* scroll in two steps of half tile size to make things smoother */
5759 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5761 Delay(wait_delay_value);
5763 /* scroll second step to align at full tile size */
5765 Delay(wait_delay_value);
5770 Delay(wait_delay_value);
5774 void RelocatePlayer(int jx, int jy, int el_player_raw)
5776 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5777 int player_nr = GET_PLAYER_NR(el_player);
5778 struct PlayerInfo *player = &stored_player[player_nr];
5779 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5780 boolean no_delay = (tape.warp_forward);
5781 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5782 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5783 int old_jx = player->jx;
5784 int old_jy = player->jy;
5785 int old_element = Feld[old_jx][old_jy];
5786 int element = Feld[jx][jy];
5787 boolean player_relocated = (old_jx != jx || old_jy != jy);
5789 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5790 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5791 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5792 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5793 int leave_side_horiz = move_dir_horiz;
5794 int leave_side_vert = move_dir_vert;
5795 int enter_side = enter_side_horiz | enter_side_vert;
5796 int leave_side = leave_side_horiz | leave_side_vert;
5798 if (player->GameOver) /* do not reanimate dead player */
5801 if (!player_relocated) /* no need to relocate the player */
5804 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5806 RemoveField(jx, jy); /* temporarily remove newly placed player */
5807 DrawLevelField(jx, jy);
5810 if (player->present)
5812 while (player->MovPos)
5814 ScrollPlayer(player, SCROLL_GO_ON);
5815 ScrollScreen(NULL, SCROLL_GO_ON);
5817 AdvanceFrameAndPlayerCounters(player->index_nr);
5822 Delay(wait_delay_value);
5825 DrawPlayer(player); /* needed here only to cleanup last field */
5826 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5828 player->is_moving = FALSE;
5831 if (IS_CUSTOM_ELEMENT(old_element))
5832 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5834 player->index_bit, leave_side);
5836 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5838 player->index_bit, leave_side);
5840 Feld[jx][jy] = el_player;
5841 InitPlayerField(jx, jy, el_player, TRUE);
5843 /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5844 possible that the relocation target field did not contain a player element,
5845 but a walkable element, to which the new player was relocated -- in this
5846 case, restore that (already initialized!) element on the player field */
5847 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5849 Feld[jx][jy] = element; /* restore previously existing element */
5851 /* !!! do not initialize already initialized element a second time !!! */
5852 /* (this causes at least problems with "element creation" CE trigger for
5853 already existing elements, and existing Sokoban fields counted twice) */
5854 InitField(jx, jy, FALSE);
5858 /* only visually relocate centered player */
5859 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5860 FALSE, level.instant_relocation);
5862 TestIfPlayerTouchesBadThing(jx, jy);
5863 TestIfPlayerTouchesCustomElement(jx, jy);
5865 if (IS_CUSTOM_ELEMENT(element))
5866 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5867 player->index_bit, enter_side);
5869 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5870 player->index_bit, enter_side);
5873 if (player->is_switching)
5875 /* ensure that relocation while still switching an element does not cause
5876 a new element to be treated as also switched directly after relocation
5877 (this is important for teleporter switches that teleport the player to
5878 a place where another teleporter switch is in the same direction, which
5879 would then incorrectly be treated as immediately switched before the
5880 direction key that caused the switch was released) */
5882 player->switch_x += jx - old_jx;
5883 player->switch_y += jy - old_jy;
5888 void Explode(int ex, int ey, int phase, int mode)
5894 /* !!! eliminate this variable !!! */
5895 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5897 if (game.explosions_delayed)
5899 ExplodeField[ex][ey] = mode;
5903 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5905 int center_element = Feld[ex][ey];
5906 int artwork_element, explosion_element; /* set these values later */
5909 /* --- This is only really needed (and now handled) in "Impact()". --- */
5910 /* do not explode moving elements that left the explode field in time */
5911 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5912 center_element == EL_EMPTY &&
5913 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5918 /* !!! at this place, the center element may be EL_BLOCKED !!! */
5919 if (mode == EX_TYPE_NORMAL ||
5920 mode == EX_TYPE_CENTER ||
5921 mode == EX_TYPE_CROSS)
5922 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5925 /* remove things displayed in background while burning dynamite */
5926 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5929 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5931 /* put moving element to center field (and let it explode there) */
5932 center_element = MovingOrBlocked2Element(ex, ey);
5933 RemoveMovingField(ex, ey);
5934 Feld[ex][ey] = center_element;
5937 /* now "center_element" is finally determined -- set related values now */
5938 artwork_element = center_element; /* for custom player artwork */
5939 explosion_element = center_element; /* for custom player artwork */
5941 if (IS_PLAYER(ex, ey))
5943 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5945 artwork_element = stored_player[player_nr].artwork_element;
5947 if (level.use_explosion_element[player_nr])
5949 explosion_element = level.explosion_element[player_nr];
5950 artwork_element = explosion_element;
5955 if (mode == EX_TYPE_NORMAL ||
5956 mode == EX_TYPE_CENTER ||
5957 mode == EX_TYPE_CROSS)
5958 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5961 last_phase = element_info[explosion_element].explosion_delay + 1;
5963 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5965 int xx = x - ex + 1;
5966 int yy = y - ey + 1;
5969 if (!IN_LEV_FIELD(x, y) ||
5970 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5971 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5974 element = Feld[x][y];
5976 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5978 element = MovingOrBlocked2Element(x, y);
5980 if (!IS_EXPLOSION_PROOF(element))
5981 RemoveMovingField(x, y);
5984 /* indestructible elements can only explode in center (but not flames) */
5985 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5986 mode == EX_TYPE_BORDER)) ||
5987 element == EL_FLAMES)
5990 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5991 behaviour, for example when touching a yamyam that explodes to rocks
5992 with active deadly shield, a rock is created under the player !!! */
5993 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5995 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5996 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5997 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5999 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
6002 if (IS_ACTIVE_BOMB(element))
6004 /* re-activate things under the bomb like gate or penguin */
6005 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
6012 /* save walkable background elements while explosion on same tile */
6013 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
6014 (x != ex || y != ey || mode == EX_TYPE_BORDER))
6015 Back[x][y] = element;
6017 /* ignite explodable elements reached by other explosion */
6018 if (element == EL_EXPLOSION)
6019 element = Store2[x][y];
6021 if (AmoebaNr[x][y] &&
6022 (element == EL_AMOEBA_FULL ||
6023 element == EL_BD_AMOEBA ||
6024 element == EL_AMOEBA_GROWING))
6026 AmoebaCnt[AmoebaNr[x][y]]--;
6027 AmoebaCnt2[AmoebaNr[x][y]]--;
6032 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
6034 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
6036 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
6038 if (PLAYERINFO(ex, ey)->use_murphy)
6039 Store[x][y] = EL_EMPTY;
6042 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
6043 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
6044 else if (ELEM_IS_PLAYER(center_element))
6045 Store[x][y] = EL_EMPTY;
6046 else if (center_element == EL_YAMYAM)
6047 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
6048 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
6049 Store[x][y] = element_info[center_element].content.e[xx][yy];
6051 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6052 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6053 otherwise) -- FIX THIS !!! */
6054 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6055 Store[x][y] = element_info[element].content.e[1][1];
6057 else if (!CAN_EXPLODE(element))
6058 Store[x][y] = element_info[element].content.e[1][1];
6061 Store[x][y] = EL_EMPTY;
6063 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6064 center_element == EL_AMOEBA_TO_DIAMOND)
6065 Store2[x][y] = element;
6067 Feld[x][y] = EL_EXPLOSION;
6068 GfxElement[x][y] = artwork_element;
6070 ExplodePhase[x][y] = 1;
6071 ExplodeDelay[x][y] = last_phase;
6076 if (center_element == EL_YAMYAM)
6077 game.yamyam_content_nr =
6078 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6090 GfxFrame[x][y] = 0; /* restart explosion animation */
6092 last_phase = ExplodeDelay[x][y];
6094 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6098 /* activate this even in non-DEBUG version until cause for crash in
6099 getGraphicAnimationFrame() (see below) is found and eliminated */
6105 /* this can happen if the player leaves an explosion just in time */
6106 if (GfxElement[x][y] == EL_UNDEFINED)
6107 GfxElement[x][y] = EL_EMPTY;
6109 if (GfxElement[x][y] == EL_UNDEFINED)
6112 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
6113 printf("Explode(): This should never happen!\n");
6116 GfxElement[x][y] = EL_EMPTY;
6122 border_element = Store2[x][y];
6123 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6124 border_element = StorePlayer[x][y];
6126 if (phase == element_info[border_element].ignition_delay ||
6127 phase == last_phase)
6129 boolean border_explosion = FALSE;
6131 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6132 !PLAYER_EXPLOSION_PROTECTED(x, y))
6134 KillPlayerUnlessExplosionProtected(x, y);
6135 border_explosion = TRUE;
6137 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6139 Feld[x][y] = Store2[x][y];
6142 border_explosion = TRUE;
6144 else if (border_element == EL_AMOEBA_TO_DIAMOND)
6146 AmoebeUmwandeln(x, y);
6148 border_explosion = TRUE;
6151 /* if an element just explodes due to another explosion (chain-reaction),
6152 do not immediately end the new explosion when it was the last frame of
6153 the explosion (as it would be done in the following "if"-statement!) */
6154 if (border_explosion && phase == last_phase)
6158 if (phase == last_phase)
6162 element = Feld[x][y] = Store[x][y];
6163 Store[x][y] = Store2[x][y] = 0;
6164 GfxElement[x][y] = EL_UNDEFINED;
6166 /* player can escape from explosions and might therefore be still alive */
6167 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6168 element <= EL_PLAYER_IS_EXPLODING_4)
6170 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6171 int explosion_element = EL_PLAYER_1 + player_nr;
6172 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6173 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6175 if (level.use_explosion_element[player_nr])
6176 explosion_element = level.explosion_element[player_nr];
6178 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6179 element_info[explosion_element].content.e[xx][yy]);
6182 /* restore probably existing indestructible background element */
6183 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6184 element = Feld[x][y] = Back[x][y];
6187 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6188 GfxDir[x][y] = MV_NONE;
6189 ChangeDelay[x][y] = 0;
6190 ChangePage[x][y] = -1;
6192 #if USE_NEW_CUSTOM_VALUE
6193 CustomValue[x][y] = 0;
6196 InitField_WithBug2(x, y, FALSE);
6198 TEST_DrawLevelField(x, y);
6200 TestIfElementTouchesCustomElement(x, y);
6202 if (GFX_CRUMBLED(element))
6203 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6205 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6206 StorePlayer[x][y] = 0;
6208 if (ELEM_IS_PLAYER(element))
6209 RelocatePlayer(x, y, element);
6211 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6213 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6214 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6217 TEST_DrawLevelFieldCrumbled(x, y);
6219 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6221 DrawLevelElement(x, y, Back[x][y]);
6222 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6224 else if (IS_WALKABLE_UNDER(Back[x][y]))
6226 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6227 DrawLevelElementThruMask(x, y, Back[x][y]);
6229 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6230 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6234 void DynaExplode(int ex, int ey)
6237 int dynabomb_element = Feld[ex][ey];
6238 int dynabomb_size = 1;
6239 boolean dynabomb_xl = FALSE;
6240 struct PlayerInfo *player;
6241 static int xy[4][2] =
6249 if (IS_ACTIVE_BOMB(dynabomb_element))
6251 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6252 dynabomb_size = player->dynabomb_size;
6253 dynabomb_xl = player->dynabomb_xl;
6254 player->dynabombs_left++;
6257 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6259 for (i = 0; i < NUM_DIRECTIONS; i++)
6261 for (j = 1; j <= dynabomb_size; j++)
6263 int x = ex + j * xy[i][0];
6264 int y = ey + j * xy[i][1];
6267 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6270 element = Feld[x][y];
6272 /* do not restart explosions of fields with active bombs */
6273 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6276 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6278 if (element != EL_EMPTY && element != EL_EXPLOSION &&
6279 !IS_DIGGABLE(element) && !dynabomb_xl)
6285 void Bang(int x, int y)
6287 int element = MovingOrBlocked2Element(x, y);
6288 int explosion_type = EX_TYPE_NORMAL;
6290 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6292 struct PlayerInfo *player = PLAYERINFO(x, y);
6294 #if USE_FIX_CE_ACTION_WITH_PLAYER
6295 element = Feld[x][y] = player->initial_element;
6297 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6298 player->element_nr);
6301 if (level.use_explosion_element[player->index_nr])
6303 int explosion_element = level.explosion_element[player->index_nr];
6305 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6306 explosion_type = EX_TYPE_CROSS;
6307 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6308 explosion_type = EX_TYPE_CENTER;
6316 case EL_BD_BUTTERFLY:
6319 case EL_DARK_YAMYAM:
6323 RaiseScoreElement(element);
6326 case EL_DYNABOMB_PLAYER_1_ACTIVE:
6327 case EL_DYNABOMB_PLAYER_2_ACTIVE:
6328 case EL_DYNABOMB_PLAYER_3_ACTIVE:
6329 case EL_DYNABOMB_PLAYER_4_ACTIVE:
6330 case EL_DYNABOMB_INCREASE_NUMBER:
6331 case EL_DYNABOMB_INCREASE_SIZE:
6332 case EL_DYNABOMB_INCREASE_POWER:
6333 explosion_type = EX_TYPE_DYNA;
6336 case EL_DC_LANDMINE:
6338 case EL_EM_EXIT_OPEN:
6339 case EL_EM_STEEL_EXIT_OPEN:
6341 explosion_type = EX_TYPE_CENTER;
6346 case EL_LAMP_ACTIVE:
6347 case EL_AMOEBA_TO_DIAMOND:
6348 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
6349 explosion_type = EX_TYPE_CENTER;
6353 if (element_info[element].explosion_type == EXPLODES_CROSS)
6354 explosion_type = EX_TYPE_CROSS;
6355 else if (element_info[element].explosion_type == EXPLODES_1X1)
6356 explosion_type = EX_TYPE_CENTER;
6360 if (explosion_type == EX_TYPE_DYNA)
6363 Explode(x, y, EX_PHASE_START, explosion_type);
6365 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6368 void SplashAcid(int x, int y)
6370 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6371 (!IN_LEV_FIELD(x - 1, y - 2) ||
6372 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6373 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6375 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6376 (!IN_LEV_FIELD(x + 1, y - 2) ||
6377 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6378 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6380 PlayLevelSound(x, y, SND_ACID_SPLASHING);
6383 static void InitBeltMovement()
6385 static int belt_base_element[4] =
6387 EL_CONVEYOR_BELT_1_LEFT,
6388 EL_CONVEYOR_BELT_2_LEFT,
6389 EL_CONVEYOR_BELT_3_LEFT,
6390 EL_CONVEYOR_BELT_4_LEFT
6392 static int belt_base_active_element[4] =
6394 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6395 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6396 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6397 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6402 /* set frame order for belt animation graphic according to belt direction */
6403 for (i = 0; i < NUM_BELTS; i++)
6407 for (j = 0; j < NUM_BELT_PARTS; j++)
6409 int element = belt_base_active_element[belt_nr] + j;
6410 int graphic_1 = el2img(element);
6411 int graphic_2 = el2panelimg(element);
6413 if (game.belt_dir[i] == MV_LEFT)
6415 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6416 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6420 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6421 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6426 SCAN_PLAYFIELD(x, y)
6428 int element = Feld[x][y];
6430 for (i = 0; i < NUM_BELTS; i++)
6432 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6434 int e_belt_nr = getBeltNrFromBeltElement(element);
6437 if (e_belt_nr == belt_nr)
6439 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6441 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6448 static void ToggleBeltSwitch(int x, int y)
6450 static int belt_base_element[4] =
6452 EL_CONVEYOR_BELT_1_LEFT,
6453 EL_CONVEYOR_BELT_2_LEFT,
6454 EL_CONVEYOR_BELT_3_LEFT,
6455 EL_CONVEYOR_BELT_4_LEFT
6457 static int belt_base_active_element[4] =
6459 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6460 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6461 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6462 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6464 static int belt_base_switch_element[4] =
6466 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6467 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6468 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6469 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6471 static int belt_move_dir[4] =
6479 int element = Feld[x][y];
6480 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6481 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6482 int belt_dir = belt_move_dir[belt_dir_nr];
6485 if (!IS_BELT_SWITCH(element))
6488 game.belt_dir_nr[belt_nr] = belt_dir_nr;
6489 game.belt_dir[belt_nr] = belt_dir;
6491 if (belt_dir_nr == 3)
6494 /* set frame order for belt animation graphic according to belt direction */
6495 for (i = 0; i < NUM_BELT_PARTS; i++)
6497 int element = belt_base_active_element[belt_nr] + i;
6498 int graphic_1 = el2img(element);
6499 int graphic_2 = el2panelimg(element);
6501 if (belt_dir == MV_LEFT)
6503 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6504 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6508 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6509 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6513 SCAN_PLAYFIELD(xx, yy)
6515 int element = Feld[xx][yy];
6517 if (IS_BELT_SWITCH(element))
6519 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6521 if (e_belt_nr == belt_nr)
6523 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6524 TEST_DrawLevelField(xx, yy);
6527 else if (IS_BELT(element) && belt_dir != MV_NONE)
6529 int e_belt_nr = getBeltNrFromBeltElement(element);
6531 if (e_belt_nr == belt_nr)
6533 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6535 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6536 TEST_DrawLevelField(xx, yy);
6539 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6541 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6543 if (e_belt_nr == belt_nr)
6545 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6547 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6548 TEST_DrawLevelField(xx, yy);
6554 static void ToggleSwitchgateSwitch(int x, int y)
6558 game.switchgate_pos = !game.switchgate_pos;
6560 SCAN_PLAYFIELD(xx, yy)
6562 int element = Feld[xx][yy];
6564 #if !USE_BOTH_SWITCHGATE_SWITCHES
6565 if (element == EL_SWITCHGATE_SWITCH_UP ||
6566 element == EL_SWITCHGATE_SWITCH_DOWN)
6568 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6569 TEST_DrawLevelField(xx, yy);
6571 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6572 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6574 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6575 TEST_DrawLevelField(xx, yy);
6578 if (element == EL_SWITCHGATE_SWITCH_UP)
6580 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6581 TEST_DrawLevelField(xx, yy);
6583 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6585 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6586 TEST_DrawLevelField(xx, yy);
6588 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6590 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6591 TEST_DrawLevelField(xx, yy);
6593 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6595 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6596 TEST_DrawLevelField(xx, yy);
6599 else if (element == EL_SWITCHGATE_OPEN ||
6600 element == EL_SWITCHGATE_OPENING)
6602 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6604 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6606 else if (element == EL_SWITCHGATE_CLOSED ||
6607 element == EL_SWITCHGATE_CLOSING)
6609 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6611 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6616 static int getInvisibleActiveFromInvisibleElement(int element)
6618 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6619 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6620 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6624 static int getInvisibleFromInvisibleActiveElement(int element)
6626 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6627 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6628 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6632 static void RedrawAllLightSwitchesAndInvisibleElements()
6636 SCAN_PLAYFIELD(x, y)
6638 int element = Feld[x][y];
6640 if (element == EL_LIGHT_SWITCH &&
6641 game.light_time_left > 0)
6643 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6644 TEST_DrawLevelField(x, y);
6646 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6647 game.light_time_left == 0)
6649 Feld[x][y] = EL_LIGHT_SWITCH;
6650 TEST_DrawLevelField(x, y);
6652 else if (element == EL_EMC_DRIPPER &&
6653 game.light_time_left > 0)
6655 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6656 TEST_DrawLevelField(x, y);
6658 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6659 game.light_time_left == 0)
6661 Feld[x][y] = EL_EMC_DRIPPER;
6662 TEST_DrawLevelField(x, y);
6664 else if (element == EL_INVISIBLE_STEELWALL ||
6665 element == EL_INVISIBLE_WALL ||
6666 element == EL_INVISIBLE_SAND)
6668 if (game.light_time_left > 0)
6669 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6671 TEST_DrawLevelField(x, y);
6673 /* uncrumble neighbour fields, if needed */
6674 if (element == EL_INVISIBLE_SAND)
6675 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6677 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6678 element == EL_INVISIBLE_WALL_ACTIVE ||
6679 element == EL_INVISIBLE_SAND_ACTIVE)
6681 if (game.light_time_left == 0)
6682 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6684 TEST_DrawLevelField(x, y);
6686 /* re-crumble neighbour fields, if needed */
6687 if (element == EL_INVISIBLE_SAND)
6688 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6693 static void RedrawAllInvisibleElementsForLenses()
6697 SCAN_PLAYFIELD(x, y)
6699 int element = Feld[x][y];
6701 if (element == EL_EMC_DRIPPER &&
6702 game.lenses_time_left > 0)
6704 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6705 TEST_DrawLevelField(x, y);
6707 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6708 game.lenses_time_left == 0)
6710 Feld[x][y] = EL_EMC_DRIPPER;
6711 TEST_DrawLevelField(x, y);
6713 else if (element == EL_INVISIBLE_STEELWALL ||
6714 element == EL_INVISIBLE_WALL ||
6715 element == EL_INVISIBLE_SAND)
6717 if (game.lenses_time_left > 0)
6718 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6720 TEST_DrawLevelField(x, y);
6722 /* uncrumble neighbour fields, if needed */
6723 if (element == EL_INVISIBLE_SAND)
6724 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6726 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6727 element == EL_INVISIBLE_WALL_ACTIVE ||
6728 element == EL_INVISIBLE_SAND_ACTIVE)
6730 if (game.lenses_time_left == 0)
6731 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6733 TEST_DrawLevelField(x, y);
6735 /* re-crumble neighbour fields, if needed */
6736 if (element == EL_INVISIBLE_SAND)
6737 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6742 static void RedrawAllInvisibleElementsForMagnifier()
6746 SCAN_PLAYFIELD(x, y)
6748 int element = Feld[x][y];
6750 if (element == EL_EMC_FAKE_GRASS &&
6751 game.magnify_time_left > 0)
6753 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6754 TEST_DrawLevelField(x, y);
6756 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6757 game.magnify_time_left == 0)
6759 Feld[x][y] = EL_EMC_FAKE_GRASS;
6760 TEST_DrawLevelField(x, y);
6762 else if (IS_GATE_GRAY(element) &&
6763 game.magnify_time_left > 0)
6765 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6766 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6767 IS_EM_GATE_GRAY(element) ?
6768 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6769 IS_EMC_GATE_GRAY(element) ?
6770 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6771 IS_DC_GATE_GRAY(element) ?
6772 EL_DC_GATE_WHITE_GRAY_ACTIVE :
6774 TEST_DrawLevelField(x, y);
6776 else if (IS_GATE_GRAY_ACTIVE(element) &&
6777 game.magnify_time_left == 0)
6779 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6780 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6781 IS_EM_GATE_GRAY_ACTIVE(element) ?
6782 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6783 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6784 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6785 IS_DC_GATE_GRAY_ACTIVE(element) ?
6786 EL_DC_GATE_WHITE_GRAY :
6788 TEST_DrawLevelField(x, y);
6793 static void ToggleLightSwitch(int x, int y)
6795 int element = Feld[x][y];
6797 game.light_time_left =
6798 (element == EL_LIGHT_SWITCH ?
6799 level.time_light * FRAMES_PER_SECOND : 0);
6801 RedrawAllLightSwitchesAndInvisibleElements();
6804 static void ActivateTimegateSwitch(int x, int y)
6808 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6810 SCAN_PLAYFIELD(xx, yy)
6812 int element = Feld[xx][yy];
6814 if (element == EL_TIMEGATE_CLOSED ||
6815 element == EL_TIMEGATE_CLOSING)
6817 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6818 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6822 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6824 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6825 TEST_DrawLevelField(xx, yy);
6832 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6833 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6835 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6839 void Impact(int x, int y)
6841 boolean last_line = (y == lev_fieldy - 1);
6842 boolean object_hit = FALSE;
6843 boolean impact = (last_line || object_hit);
6844 int element = Feld[x][y];
6845 int smashed = EL_STEELWALL;
6847 if (!last_line) /* check if element below was hit */
6849 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6852 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6853 MovDir[x][y + 1] != MV_DOWN ||
6854 MovPos[x][y + 1] <= TILEY / 2));
6856 /* do not smash moving elements that left the smashed field in time */
6857 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6858 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6861 #if USE_QUICKSAND_IMPACT_BUGFIX
6862 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6864 RemoveMovingField(x, y + 1);
6865 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6866 Feld[x][y + 2] = EL_ROCK;
6867 TEST_DrawLevelField(x, y + 2);
6872 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6874 RemoveMovingField(x, y + 1);
6875 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6876 Feld[x][y + 2] = EL_ROCK;
6877 TEST_DrawLevelField(x, y + 2);
6884 smashed = MovingOrBlocked2Element(x, y + 1);
6886 impact = (last_line || object_hit);
6889 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6891 SplashAcid(x, y + 1);
6895 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6896 /* only reset graphic animation if graphic really changes after impact */
6898 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6900 ResetGfxAnimation(x, y);
6901 TEST_DrawLevelField(x, y);
6904 if (impact && CAN_EXPLODE_IMPACT(element))
6909 else if (impact && element == EL_PEARL &&
6910 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6912 ResetGfxAnimation(x, y);
6914 Feld[x][y] = EL_PEARL_BREAKING;
6915 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6918 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6920 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6925 if (impact && element == EL_AMOEBA_DROP)
6927 if (object_hit && IS_PLAYER(x, y + 1))
6928 KillPlayerUnlessEnemyProtected(x, y + 1);
6929 else if (object_hit && smashed == EL_PENGUIN)
6933 Feld[x][y] = EL_AMOEBA_GROWING;
6934 Store[x][y] = EL_AMOEBA_WET;
6936 ResetRandomAnimationValue(x, y);
6941 if (object_hit) /* check which object was hit */
6943 if ((CAN_PASS_MAGIC_WALL(element) &&
6944 (smashed == EL_MAGIC_WALL ||
6945 smashed == EL_BD_MAGIC_WALL)) ||
6946 (CAN_PASS_DC_MAGIC_WALL(element) &&
6947 smashed == EL_DC_MAGIC_WALL))
6950 int activated_magic_wall =
6951 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6952 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6953 EL_DC_MAGIC_WALL_ACTIVE);
6955 /* activate magic wall / mill */
6956 SCAN_PLAYFIELD(xx, yy)
6958 if (Feld[xx][yy] == smashed)
6959 Feld[xx][yy] = activated_magic_wall;
6962 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6963 game.magic_wall_active = TRUE;
6965 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6966 SND_MAGIC_WALL_ACTIVATING :
6967 smashed == EL_BD_MAGIC_WALL ?
6968 SND_BD_MAGIC_WALL_ACTIVATING :
6969 SND_DC_MAGIC_WALL_ACTIVATING));
6972 if (IS_PLAYER(x, y + 1))
6974 if (CAN_SMASH_PLAYER(element))
6976 KillPlayerUnlessEnemyProtected(x, y + 1);
6980 else if (smashed == EL_PENGUIN)
6982 if (CAN_SMASH_PLAYER(element))
6988 else if (element == EL_BD_DIAMOND)
6990 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6996 else if (((element == EL_SP_INFOTRON ||
6997 element == EL_SP_ZONK) &&
6998 (smashed == EL_SP_SNIKSNAK ||
6999 smashed == EL_SP_ELECTRON ||
7000 smashed == EL_SP_DISK_ORANGE)) ||
7001 (element == EL_SP_INFOTRON &&
7002 smashed == EL_SP_DISK_YELLOW))
7007 else if (CAN_SMASH_EVERYTHING(element))
7009 if (IS_CLASSIC_ENEMY(smashed) ||
7010 CAN_EXPLODE_SMASHED(smashed))
7015 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
7017 if (smashed == EL_LAMP ||
7018 smashed == EL_LAMP_ACTIVE)
7023 else if (smashed == EL_NUT)
7025 Feld[x][y + 1] = EL_NUT_BREAKING;
7026 PlayLevelSound(x, y, SND_NUT_BREAKING);
7027 RaiseScoreElement(EL_NUT);
7030 else if (smashed == EL_PEARL)
7032 ResetGfxAnimation(x, y);
7034 Feld[x][y + 1] = EL_PEARL_BREAKING;
7035 PlayLevelSound(x, y, SND_PEARL_BREAKING);
7038 else if (smashed == EL_DIAMOND)
7040 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
7041 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
7044 else if (IS_BELT_SWITCH(smashed))
7046 ToggleBeltSwitch(x, y + 1);
7048 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
7049 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
7050 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
7051 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
7053 ToggleSwitchgateSwitch(x, y + 1);
7055 else if (smashed == EL_LIGHT_SWITCH ||
7056 smashed == EL_LIGHT_SWITCH_ACTIVE)
7058 ToggleLightSwitch(x, y + 1);
7063 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
7066 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7068 CheckElementChangeBySide(x, y + 1, smashed, element,
7069 CE_SWITCHED, CH_SIDE_TOP);
7070 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7076 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7081 /* play sound of magic wall / mill */
7083 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7084 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7085 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7087 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7088 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7089 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7090 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7091 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7092 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7097 /* play sound of object that hits the ground */
7098 if (last_line || object_hit)
7099 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7102 inline static void TurnRoundExt(int x, int y)
7114 { 0, 0 }, { 0, 0 }, { 0, 0 },
7119 int left, right, back;
7123 { MV_DOWN, MV_UP, MV_RIGHT },
7124 { MV_UP, MV_DOWN, MV_LEFT },
7126 { MV_LEFT, MV_RIGHT, MV_DOWN },
7130 { MV_RIGHT, MV_LEFT, MV_UP }
7133 int element = Feld[x][y];
7134 int move_pattern = element_info[element].move_pattern;
7136 int old_move_dir = MovDir[x][y];
7137 int left_dir = turn[old_move_dir].left;
7138 int right_dir = turn[old_move_dir].right;
7139 int back_dir = turn[old_move_dir].back;
7141 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
7142 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
7143 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
7144 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
7146 int left_x = x + left_dx, left_y = y + left_dy;
7147 int right_x = x + right_dx, right_y = y + right_dy;
7148 int move_x = x + move_dx, move_y = y + move_dy;
7152 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7154 TestIfBadThingTouchesOtherBadThing(x, y);
7156 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7157 MovDir[x][y] = right_dir;
7158 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7159 MovDir[x][y] = left_dir;
7161 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7163 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
7166 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7168 TestIfBadThingTouchesOtherBadThing(x, y);
7170 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7171 MovDir[x][y] = left_dir;
7172 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7173 MovDir[x][y] = right_dir;
7175 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7177 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
7180 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7182 TestIfBadThingTouchesOtherBadThing(x, y);
7184 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7185 MovDir[x][y] = left_dir;
7186 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7187 MovDir[x][y] = right_dir;
7189 if (MovDir[x][y] != old_move_dir)
7192 else if (element == EL_YAMYAM)
7194 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7195 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7197 if (can_turn_left && can_turn_right)
7198 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7199 else if (can_turn_left)
7200 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7201 else if (can_turn_right)
7202 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7204 MovDir[x][y] = back_dir;
7206 MovDelay[x][y] = 16 + 16 * RND(3);
7208 else if (element == EL_DARK_YAMYAM)
7210 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7212 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7215 if (can_turn_left && can_turn_right)
7216 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7217 else if (can_turn_left)
7218 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7219 else if (can_turn_right)
7220 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7222 MovDir[x][y] = back_dir;
7224 MovDelay[x][y] = 16 + 16 * RND(3);
7226 else if (element == EL_PACMAN)
7228 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7229 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7231 if (can_turn_left && can_turn_right)
7232 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7233 else if (can_turn_left)
7234 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7235 else if (can_turn_right)
7236 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7238 MovDir[x][y] = back_dir;
7240 MovDelay[x][y] = 6 + RND(40);
7242 else if (element == EL_PIG)
7244 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7245 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7246 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7247 boolean should_turn_left, should_turn_right, should_move_on;
7249 int rnd = RND(rnd_value);
7251 should_turn_left = (can_turn_left &&
7253 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7254 y + back_dy + left_dy)));
7255 should_turn_right = (can_turn_right &&
7257 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7258 y + back_dy + right_dy)));
7259 should_move_on = (can_move_on &&
7262 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7263 y + move_dy + left_dy) ||
7264 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7265 y + move_dy + right_dy)));
7267 if (should_turn_left || should_turn_right || should_move_on)
7269 if (should_turn_left && should_turn_right && should_move_on)
7270 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
7271 rnd < 2 * rnd_value / 3 ? right_dir :
7273 else if (should_turn_left && should_turn_right)
7274 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7275 else if (should_turn_left && should_move_on)
7276 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7277 else if (should_turn_right && should_move_on)
7278 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7279 else if (should_turn_left)
7280 MovDir[x][y] = left_dir;
7281 else if (should_turn_right)
7282 MovDir[x][y] = right_dir;
7283 else if (should_move_on)
7284 MovDir[x][y] = old_move_dir;
7286 else if (can_move_on && rnd > rnd_value / 8)
7287 MovDir[x][y] = old_move_dir;
7288 else if (can_turn_left && can_turn_right)
7289 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7290 else if (can_turn_left && rnd > rnd_value / 8)
7291 MovDir[x][y] = left_dir;
7292 else if (can_turn_right && rnd > rnd_value/8)
7293 MovDir[x][y] = right_dir;
7295 MovDir[x][y] = back_dir;
7297 xx = x + move_xy[MovDir[x][y]].dx;
7298 yy = y + move_xy[MovDir[x][y]].dy;
7300 if (!IN_LEV_FIELD(xx, yy) ||
7301 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7302 MovDir[x][y] = old_move_dir;
7306 else if (element == EL_DRAGON)
7308 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7309 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7310 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7312 int rnd = RND(rnd_value);
7314 if (can_move_on && rnd > rnd_value / 8)
7315 MovDir[x][y] = old_move_dir;
7316 else if (can_turn_left && can_turn_right)
7317 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7318 else if (can_turn_left && rnd > rnd_value / 8)
7319 MovDir[x][y] = left_dir;
7320 else if (can_turn_right && rnd > rnd_value / 8)
7321 MovDir[x][y] = right_dir;
7323 MovDir[x][y] = back_dir;
7325 xx = x + move_xy[MovDir[x][y]].dx;
7326 yy = y + move_xy[MovDir[x][y]].dy;
7328 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7329 MovDir[x][y] = old_move_dir;
7333 else if (element == EL_MOLE)
7335 boolean can_move_on =
7336 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7337 IS_AMOEBOID(Feld[move_x][move_y]) ||
7338 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7341 boolean can_turn_left =
7342 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7343 IS_AMOEBOID(Feld[left_x][left_y])));
7345 boolean can_turn_right =
7346 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7347 IS_AMOEBOID(Feld[right_x][right_y])));
7349 if (can_turn_left && can_turn_right)
7350 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7351 else if (can_turn_left)
7352 MovDir[x][y] = left_dir;
7354 MovDir[x][y] = right_dir;
7357 if (MovDir[x][y] != old_move_dir)
7360 else if (element == EL_BALLOON)
7362 MovDir[x][y] = game.wind_direction;
7365 else if (element == EL_SPRING)
7367 #if USE_NEW_SPRING_BUMPER
7368 if (MovDir[x][y] & MV_HORIZONTAL)
7370 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7371 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7373 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7374 ResetGfxAnimation(move_x, move_y);
7375 TEST_DrawLevelField(move_x, move_y);
7377 MovDir[x][y] = back_dir;
7379 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7380 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7381 MovDir[x][y] = MV_NONE;
7384 if (MovDir[x][y] & MV_HORIZONTAL &&
7385 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7386 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7387 MovDir[x][y] = MV_NONE;
7392 else if (element == EL_ROBOT ||
7393 element == EL_SATELLITE ||
7394 element == EL_PENGUIN ||
7395 element == EL_EMC_ANDROID)
7397 int attr_x = -1, attr_y = -1;
7408 for (i = 0; i < MAX_PLAYERS; i++)
7410 struct PlayerInfo *player = &stored_player[i];
7411 int jx = player->jx, jy = player->jy;
7413 if (!player->active)
7417 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7425 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7426 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7427 game.engine_version < VERSION_IDENT(3,1,0,0)))
7433 if (element == EL_PENGUIN)
7436 static int xy[4][2] =
7444 for (i = 0; i < NUM_DIRECTIONS; i++)
7446 int ex = x + xy[i][0];
7447 int ey = y + xy[i][1];
7449 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7450 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7451 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7452 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7461 MovDir[x][y] = MV_NONE;
7463 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7464 else if (attr_x > x)
7465 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7467 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7468 else if (attr_y > y)
7469 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7471 if (element == EL_ROBOT)
7475 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7476 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7477 Moving2Blocked(x, y, &newx, &newy);
7479 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7480 MovDelay[x][y] = 8 + 8 * !RND(3);
7482 MovDelay[x][y] = 16;
7484 else if (element == EL_PENGUIN)
7490 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7492 boolean first_horiz = RND(2);
7493 int new_move_dir = MovDir[x][y];
7496 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7497 Moving2Blocked(x, y, &newx, &newy);
7499 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7503 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7504 Moving2Blocked(x, y, &newx, &newy);
7506 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7509 MovDir[x][y] = old_move_dir;
7513 else if (element == EL_SATELLITE)
7519 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7521 boolean first_horiz = RND(2);
7522 int new_move_dir = MovDir[x][y];
7525 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7526 Moving2Blocked(x, y, &newx, &newy);
7528 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7532 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7533 Moving2Blocked(x, y, &newx, &newy);
7535 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7538 MovDir[x][y] = old_move_dir;
7542 else if (element == EL_EMC_ANDROID)
7544 static int check_pos[16] =
7546 -1, /* 0 => (invalid) */
7547 7, /* 1 => MV_LEFT */
7548 3, /* 2 => MV_RIGHT */
7549 -1, /* 3 => (invalid) */
7551 0, /* 5 => MV_LEFT | MV_UP */
7552 2, /* 6 => MV_RIGHT | MV_UP */
7553 -1, /* 7 => (invalid) */
7554 5, /* 8 => MV_DOWN */
7555 6, /* 9 => MV_LEFT | MV_DOWN */
7556 4, /* 10 => MV_RIGHT | MV_DOWN */
7557 -1, /* 11 => (invalid) */
7558 -1, /* 12 => (invalid) */
7559 -1, /* 13 => (invalid) */
7560 -1, /* 14 => (invalid) */
7561 -1, /* 15 => (invalid) */
7569 { -1, -1, MV_LEFT | MV_UP },
7571 { +1, -1, MV_RIGHT | MV_UP },
7572 { +1, 0, MV_RIGHT },
7573 { +1, +1, MV_RIGHT | MV_DOWN },
7575 { -1, +1, MV_LEFT | MV_DOWN },
7578 int start_pos, check_order;
7579 boolean can_clone = FALSE;
7582 /* check if there is any free field around current position */
7583 for (i = 0; i < 8; i++)
7585 int newx = x + check_xy[i].dx;
7586 int newy = y + check_xy[i].dy;
7588 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7596 if (can_clone) /* randomly find an element to clone */
7600 start_pos = check_pos[RND(8)];
7601 check_order = (RND(2) ? -1 : +1);
7603 for (i = 0; i < 8; i++)
7605 int pos_raw = start_pos + i * check_order;
7606 int pos = (pos_raw + 8) % 8;
7607 int newx = x + check_xy[pos].dx;
7608 int newy = y + check_xy[pos].dy;
7610 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7612 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7613 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7615 Store[x][y] = Feld[newx][newy];
7624 if (can_clone) /* randomly find a direction to move */
7628 start_pos = check_pos[RND(8)];
7629 check_order = (RND(2) ? -1 : +1);
7631 for (i = 0; i < 8; i++)
7633 int pos_raw = start_pos + i * check_order;
7634 int pos = (pos_raw + 8) % 8;
7635 int newx = x + check_xy[pos].dx;
7636 int newy = y + check_xy[pos].dy;
7637 int new_move_dir = check_xy[pos].dir;
7639 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7641 MovDir[x][y] = new_move_dir;
7642 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7651 if (can_clone) /* cloning and moving successful */
7654 /* cannot clone -- try to move towards player */
7656 start_pos = check_pos[MovDir[x][y] & 0x0f];
7657 check_order = (RND(2) ? -1 : +1);
7659 for (i = 0; i < 3; i++)
7661 /* first check start_pos, then previous/next or (next/previous) pos */
7662 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7663 int pos = (pos_raw + 8) % 8;
7664 int newx = x + check_xy[pos].dx;
7665 int newy = y + check_xy[pos].dy;
7666 int new_move_dir = check_xy[pos].dir;
7668 if (IS_PLAYER(newx, newy))
7671 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7673 MovDir[x][y] = new_move_dir;
7674 MovDelay[x][y] = level.android_move_time * 8 + 1;
7681 else if (move_pattern == MV_TURNING_LEFT ||
7682 move_pattern == MV_TURNING_RIGHT ||
7683 move_pattern == MV_TURNING_LEFT_RIGHT ||
7684 move_pattern == MV_TURNING_RIGHT_LEFT ||
7685 move_pattern == MV_TURNING_RANDOM ||
7686 move_pattern == MV_ALL_DIRECTIONS)
7688 boolean can_turn_left =
7689 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7690 boolean can_turn_right =
7691 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7693 if (element_info[element].move_stepsize == 0) /* "not moving" */
7696 if (move_pattern == MV_TURNING_LEFT)
7697 MovDir[x][y] = left_dir;
7698 else if (move_pattern == MV_TURNING_RIGHT)
7699 MovDir[x][y] = right_dir;
7700 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7701 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7702 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7703 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7704 else if (move_pattern == MV_TURNING_RANDOM)
7705 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7706 can_turn_right && !can_turn_left ? right_dir :
7707 RND(2) ? left_dir : right_dir);
7708 else if (can_turn_left && can_turn_right)
7709 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7710 else if (can_turn_left)
7711 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7712 else if (can_turn_right)
7713 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7715 MovDir[x][y] = back_dir;
7717 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7719 else if (move_pattern == MV_HORIZONTAL ||
7720 move_pattern == MV_VERTICAL)
7722 if (move_pattern & old_move_dir)
7723 MovDir[x][y] = back_dir;
7724 else if (move_pattern == MV_HORIZONTAL)
7725 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7726 else if (move_pattern == MV_VERTICAL)
7727 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7729 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7731 else if (move_pattern & MV_ANY_DIRECTION)
7733 MovDir[x][y] = move_pattern;
7734 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7736 else if (move_pattern & MV_WIND_DIRECTION)
7738 MovDir[x][y] = game.wind_direction;
7739 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7741 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7743 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7744 MovDir[x][y] = left_dir;
7745 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7746 MovDir[x][y] = right_dir;
7748 if (MovDir[x][y] != old_move_dir)
7749 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7751 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7753 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7754 MovDir[x][y] = right_dir;
7755 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7756 MovDir[x][y] = left_dir;
7758 if (MovDir[x][y] != old_move_dir)
7759 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7761 else if (move_pattern == MV_TOWARDS_PLAYER ||
7762 move_pattern == MV_AWAY_FROM_PLAYER)
7764 int attr_x = -1, attr_y = -1;
7766 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7777 for (i = 0; i < MAX_PLAYERS; i++)
7779 struct PlayerInfo *player = &stored_player[i];
7780 int jx = player->jx, jy = player->jy;
7782 if (!player->active)
7786 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7794 MovDir[x][y] = MV_NONE;
7796 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7797 else if (attr_x > x)
7798 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7800 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7801 else if (attr_y > y)
7802 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7804 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7806 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7808 boolean first_horiz = RND(2);
7809 int new_move_dir = MovDir[x][y];
7811 if (element_info[element].move_stepsize == 0) /* "not moving" */
7813 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7814 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7820 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7821 Moving2Blocked(x, y, &newx, &newy);
7823 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7827 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7828 Moving2Blocked(x, y, &newx, &newy);
7830 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7833 MovDir[x][y] = old_move_dir;
7836 else if (move_pattern == MV_WHEN_PUSHED ||
7837 move_pattern == MV_WHEN_DROPPED)
7839 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7840 MovDir[x][y] = MV_NONE;
7844 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7846 static int test_xy[7][2] =
7856 static int test_dir[7] =
7866 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7867 int move_preference = -1000000; /* start with very low preference */
7868 int new_move_dir = MV_NONE;
7869 int start_test = RND(4);
7872 for (i = 0; i < NUM_DIRECTIONS; i++)
7874 int move_dir = test_dir[start_test + i];
7875 int move_dir_preference;
7877 xx = x + test_xy[start_test + i][0];
7878 yy = y + test_xy[start_test + i][1];
7880 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7881 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7883 new_move_dir = move_dir;
7888 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7891 move_dir_preference = -1 * RunnerVisit[xx][yy];
7892 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7893 move_dir_preference = PlayerVisit[xx][yy];
7895 if (move_dir_preference > move_preference)
7897 /* prefer field that has not been visited for the longest time */
7898 move_preference = move_dir_preference;
7899 new_move_dir = move_dir;
7901 else if (move_dir_preference == move_preference &&
7902 move_dir == old_move_dir)
7904 /* prefer last direction when all directions are preferred equally */
7905 move_preference = move_dir_preference;
7906 new_move_dir = move_dir;
7910 MovDir[x][y] = new_move_dir;
7911 if (old_move_dir != new_move_dir)
7912 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7916 static void TurnRound(int x, int y)
7918 int direction = MovDir[x][y];
7922 GfxDir[x][y] = MovDir[x][y];
7924 if (direction != MovDir[x][y])
7928 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7930 ResetGfxFrame(x, y, FALSE);
7933 static boolean JustBeingPushed(int x, int y)
7937 for (i = 0; i < MAX_PLAYERS; i++)
7939 struct PlayerInfo *player = &stored_player[i];
7941 if (player->active && player->is_pushing && player->MovPos)
7943 int next_jx = player->jx + (player->jx - player->last_jx);
7944 int next_jy = player->jy + (player->jy - player->last_jy);
7946 if (x == next_jx && y == next_jy)
7954 void StartMoving(int x, int y)
7956 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7957 int element = Feld[x][y];
7962 if (MovDelay[x][y] == 0)
7963 GfxAction[x][y] = ACTION_DEFAULT;
7965 if (CAN_FALL(element) && y < lev_fieldy - 1)
7967 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7968 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7969 if (JustBeingPushed(x, y))
7972 if (element == EL_QUICKSAND_FULL)
7974 if (IS_FREE(x, y + 1))
7976 InitMovingField(x, y, MV_DOWN);
7977 started_moving = TRUE;
7979 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7980 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7981 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7982 Store[x][y] = EL_ROCK;
7984 Store[x][y] = EL_ROCK;
7987 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7989 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7991 if (!MovDelay[x][y])
7993 MovDelay[x][y] = TILEY + 1;
7995 ResetGfxAnimation(x, y);
7996 ResetGfxAnimation(x, y + 1);
8001 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
8002 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8009 Feld[x][y] = EL_QUICKSAND_EMPTY;
8010 Feld[x][y + 1] = EL_QUICKSAND_FULL;
8011 Store[x][y + 1] = Store[x][y];
8014 PlayLevelSoundAction(x, y, ACTION_FILLING);
8016 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8018 if (!MovDelay[x][y])
8020 MovDelay[x][y] = TILEY + 1;
8022 ResetGfxAnimation(x, y);
8023 ResetGfxAnimation(x, y + 1);
8028 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
8029 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8036 Feld[x][y] = EL_QUICKSAND_EMPTY;
8037 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8038 Store[x][y + 1] = Store[x][y];
8041 PlayLevelSoundAction(x, y, ACTION_FILLING);
8044 else if (element == EL_QUICKSAND_FAST_FULL)
8046 if (IS_FREE(x, y + 1))
8048 InitMovingField(x, y, MV_DOWN);
8049 started_moving = TRUE;
8051 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
8052 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8053 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8054 Store[x][y] = EL_ROCK;
8056 Store[x][y] = EL_ROCK;
8059 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8061 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8063 if (!MovDelay[x][y])
8065 MovDelay[x][y] = TILEY + 1;
8067 ResetGfxAnimation(x, y);
8068 ResetGfxAnimation(x, y + 1);
8073 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8074 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8081 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8082 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8083 Store[x][y + 1] = Store[x][y];
8086 PlayLevelSoundAction(x, y, ACTION_FILLING);
8088 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8090 if (!MovDelay[x][y])
8092 MovDelay[x][y] = TILEY + 1;
8094 ResetGfxAnimation(x, y);
8095 ResetGfxAnimation(x, y + 1);
8100 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8101 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8108 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8109 Feld[x][y + 1] = EL_QUICKSAND_FULL;
8110 Store[x][y + 1] = Store[x][y];
8113 PlayLevelSoundAction(x, y, ACTION_FILLING);
8116 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8117 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8119 InitMovingField(x, y, MV_DOWN);
8120 started_moving = TRUE;
8122 Feld[x][y] = EL_QUICKSAND_FILLING;
8123 Store[x][y] = element;
8125 PlayLevelSoundAction(x, y, ACTION_FILLING);
8127 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8128 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8130 InitMovingField(x, y, MV_DOWN);
8131 started_moving = TRUE;
8133 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
8134 Store[x][y] = element;
8136 PlayLevelSoundAction(x, y, ACTION_FILLING);
8138 else if (element == EL_MAGIC_WALL_FULL)
8140 if (IS_FREE(x, y + 1))
8142 InitMovingField(x, y, MV_DOWN);
8143 started_moving = TRUE;
8145 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
8146 Store[x][y] = EL_CHANGED(Store[x][y]);
8148 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8150 if (!MovDelay[x][y])
8151 MovDelay[x][y] = TILEY / 4 + 1;
8160 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
8161 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
8162 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8166 else if (element == EL_BD_MAGIC_WALL_FULL)
8168 if (IS_FREE(x, y + 1))
8170 InitMovingField(x, y, MV_DOWN);
8171 started_moving = TRUE;
8173 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8174 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8176 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8178 if (!MovDelay[x][y])
8179 MovDelay[x][y] = TILEY / 4 + 1;
8188 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8189 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8190 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8194 else if (element == EL_DC_MAGIC_WALL_FULL)
8196 if (IS_FREE(x, y + 1))
8198 InitMovingField(x, y, MV_DOWN);
8199 started_moving = TRUE;
8201 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8202 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8204 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8206 if (!MovDelay[x][y])
8207 MovDelay[x][y] = TILEY / 4 + 1;
8216 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8217 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8218 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8222 else if ((CAN_PASS_MAGIC_WALL(element) &&
8223 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8224 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8225 (CAN_PASS_DC_MAGIC_WALL(element) &&
8226 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8229 InitMovingField(x, y, MV_DOWN);
8230 started_moving = TRUE;
8233 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8234 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8235 EL_DC_MAGIC_WALL_FILLING);
8236 Store[x][y] = element;
8238 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8240 SplashAcid(x, y + 1);
8242 InitMovingField(x, y, MV_DOWN);
8243 started_moving = TRUE;
8245 Store[x][y] = EL_ACID;
8248 #if USE_FIX_IMPACT_COLLISION
8249 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8250 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8252 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8253 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8255 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8256 CAN_FALL(element) && WasJustFalling[x][y] &&
8257 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8259 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8260 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8261 (Feld[x][y + 1] == EL_BLOCKED)))
8263 /* this is needed for a special case not covered by calling "Impact()"
8264 from "ContinueMoving()": if an element moves to a tile directly below
8265 another element which was just falling on that tile (which was empty
8266 in the previous frame), the falling element above would just stop
8267 instead of smashing the element below (in previous version, the above
8268 element was just checked for "moving" instead of "falling", resulting
8269 in incorrect smashes caused by horizontal movement of the above
8270 element; also, the case of the player being the element to smash was
8271 simply not covered here... :-/ ) */
8273 CheckCollision[x][y] = 0;
8274 CheckImpact[x][y] = 0;
8278 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8280 if (MovDir[x][y] == MV_NONE)
8282 InitMovingField(x, y, MV_DOWN);
8283 started_moving = TRUE;
8286 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8288 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8289 MovDir[x][y] = MV_DOWN;
8291 InitMovingField(x, y, MV_DOWN);
8292 started_moving = TRUE;
8294 else if (element == EL_AMOEBA_DROP)
8296 Feld[x][y] = EL_AMOEBA_GROWING;
8297 Store[x][y] = EL_AMOEBA_WET;
8299 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8300 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8301 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8302 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8304 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
8305 (IS_FREE(x - 1, y + 1) ||
8306 Feld[x - 1][y + 1] == EL_ACID));
8307 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8308 (IS_FREE(x + 1, y + 1) ||
8309 Feld[x + 1][y + 1] == EL_ACID));
8310 boolean can_fall_any = (can_fall_left || can_fall_right);
8311 boolean can_fall_both = (can_fall_left && can_fall_right);
8312 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8314 #if USE_NEW_ALL_SLIPPERY
8315 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8317 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8318 can_fall_right = FALSE;
8319 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8320 can_fall_left = FALSE;
8321 else if (slippery_type == SLIPPERY_ONLY_LEFT)
8322 can_fall_right = FALSE;
8323 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8324 can_fall_left = FALSE;
8326 can_fall_any = (can_fall_left || can_fall_right);
8327 can_fall_both = FALSE;
8330 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8332 if (slippery_type == SLIPPERY_ONLY_LEFT)
8333 can_fall_right = FALSE;
8334 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8335 can_fall_left = FALSE;
8336 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8337 can_fall_right = FALSE;
8338 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8339 can_fall_left = FALSE;
8341 can_fall_any = (can_fall_left || can_fall_right);
8342 can_fall_both = (can_fall_left && can_fall_right);
8346 #if USE_NEW_ALL_SLIPPERY
8348 #if USE_NEW_SP_SLIPPERY
8349 /* !!! better use the same properties as for custom elements here !!! */
8350 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8351 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8353 can_fall_right = FALSE; /* slip down on left side */
8354 can_fall_both = FALSE;
8359 #if USE_NEW_ALL_SLIPPERY
8362 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8363 can_fall_right = FALSE; /* slip down on left side */
8365 can_fall_left = !(can_fall_right = RND(2));
8367 can_fall_both = FALSE;
8372 if (game.emulation == EMU_BOULDERDASH ||
8373 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8374 can_fall_right = FALSE; /* slip down on left side */
8376 can_fall_left = !(can_fall_right = RND(2));
8378 can_fall_both = FALSE;
8384 /* if not determined otherwise, prefer left side for slipping down */
8385 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8386 started_moving = TRUE;
8390 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8392 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8395 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
8396 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8397 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8398 int belt_dir = game.belt_dir[belt_nr];
8400 if ((belt_dir == MV_LEFT && left_is_free) ||
8401 (belt_dir == MV_RIGHT && right_is_free))
8403 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8405 InitMovingField(x, y, belt_dir);
8406 started_moving = TRUE;
8408 Pushed[x][y] = TRUE;
8409 Pushed[nextx][y] = TRUE;
8411 GfxAction[x][y] = ACTION_DEFAULT;
8415 MovDir[x][y] = 0; /* if element was moving, stop it */
8420 /* not "else if" because of elements that can fall and move (EL_SPRING) */
8422 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8424 if (CAN_MOVE(element) && !started_moving)
8427 int move_pattern = element_info[element].move_pattern;
8432 if (MovDir[x][y] == MV_NONE)
8434 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8435 x, y, element, element_info[element].token_name);
8436 printf("StartMoving(): This should never happen!\n");
8441 Moving2Blocked(x, y, &newx, &newy);
8443 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8446 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8447 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8449 WasJustMoving[x][y] = 0;
8450 CheckCollision[x][y] = 0;
8452 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8454 if (Feld[x][y] != element) /* element has changed */
8458 if (!MovDelay[x][y]) /* start new movement phase */
8460 /* all objects that can change their move direction after each step
8461 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8463 if (element != EL_YAMYAM &&
8464 element != EL_DARK_YAMYAM &&
8465 element != EL_PACMAN &&
8466 !(move_pattern & MV_ANY_DIRECTION) &&
8467 move_pattern != MV_TURNING_LEFT &&
8468 move_pattern != MV_TURNING_RIGHT &&
8469 move_pattern != MV_TURNING_LEFT_RIGHT &&
8470 move_pattern != MV_TURNING_RIGHT_LEFT &&
8471 move_pattern != MV_TURNING_RANDOM)
8475 if (MovDelay[x][y] && (element == EL_BUG ||
8476 element == EL_SPACESHIP ||
8477 element == EL_SP_SNIKSNAK ||
8478 element == EL_SP_ELECTRON ||
8479 element == EL_MOLE))
8480 TEST_DrawLevelField(x, y);
8484 if (MovDelay[x][y]) /* wait some time before next movement */
8488 if (element == EL_ROBOT ||
8489 element == EL_YAMYAM ||
8490 element == EL_DARK_YAMYAM)
8492 DrawLevelElementAnimationIfNeeded(x, y, element);
8493 PlayLevelSoundAction(x, y, ACTION_WAITING);
8495 else if (element == EL_SP_ELECTRON)
8496 DrawLevelElementAnimationIfNeeded(x, y, element);
8497 else if (element == EL_DRAGON)
8500 int dir = MovDir[x][y];
8501 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8502 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
8503 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
8504 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
8505 dir == MV_UP ? IMG_FLAMES_1_UP :
8506 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8507 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8509 GfxAction[x][y] = ACTION_ATTACKING;
8511 if (IS_PLAYER(x, y))
8512 DrawPlayerField(x, y);
8514 TEST_DrawLevelField(x, y);
8516 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8518 for (i = 1; i <= 3; i++)
8520 int xx = x + i * dx;
8521 int yy = y + i * dy;
8522 int sx = SCREENX(xx);
8523 int sy = SCREENY(yy);
8524 int flame_graphic = graphic + (i - 1);
8526 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8531 int flamed = MovingOrBlocked2Element(xx, yy);
8535 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8537 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8538 RemoveMovingField(xx, yy);
8540 RemoveField(xx, yy);
8542 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8545 RemoveMovingField(xx, yy);
8548 ChangeDelay[xx][yy] = 0;
8550 Feld[xx][yy] = EL_FLAMES;
8552 if (IN_SCR_FIELD(sx, sy))
8554 TEST_DrawLevelFieldCrumbled(xx, yy);
8555 DrawGraphic(sx, sy, flame_graphic, frame);
8560 if (Feld[xx][yy] == EL_FLAMES)
8561 Feld[xx][yy] = EL_EMPTY;
8562 TEST_DrawLevelField(xx, yy);
8567 if (MovDelay[x][y]) /* element still has to wait some time */
8569 PlayLevelSoundAction(x, y, ACTION_WAITING);
8575 /* now make next step */
8577 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8579 if (DONT_COLLIDE_WITH(element) &&
8580 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8581 !PLAYER_ENEMY_PROTECTED(newx, newy))
8583 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8588 else if (CAN_MOVE_INTO_ACID(element) &&
8589 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8590 !IS_MV_DIAGONAL(MovDir[x][y]) &&
8591 (MovDir[x][y] == MV_DOWN ||
8592 game.engine_version >= VERSION_IDENT(3,1,0,0)))
8594 SplashAcid(newx, newy);
8595 Store[x][y] = EL_ACID;
8597 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8599 if (Feld[newx][newy] == EL_EXIT_OPEN ||
8600 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8601 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8602 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8605 TEST_DrawLevelField(x, y);
8607 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8608 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8609 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8611 local_player->friends_still_needed--;
8612 if (!local_player->friends_still_needed &&
8613 !local_player->GameOver && AllPlayersGone)
8614 PlayerWins(local_player);
8618 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8620 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8621 TEST_DrawLevelField(newx, newy);
8623 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8625 else if (!IS_FREE(newx, newy))
8627 GfxAction[x][y] = ACTION_WAITING;
8629 if (IS_PLAYER(x, y))
8630 DrawPlayerField(x, y);
8632 TEST_DrawLevelField(x, y);
8637 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8639 if (IS_FOOD_PIG(Feld[newx][newy]))
8641 if (IS_MOVING(newx, newy))
8642 RemoveMovingField(newx, newy);
8645 Feld[newx][newy] = EL_EMPTY;
8646 TEST_DrawLevelField(newx, newy);
8649 PlayLevelSound(x, y, SND_PIG_DIGGING);
8651 else if (!IS_FREE(newx, newy))
8653 if (IS_PLAYER(x, y))
8654 DrawPlayerField(x, y);
8656 TEST_DrawLevelField(x, y);
8661 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8663 if (Store[x][y] != EL_EMPTY)
8665 boolean can_clone = FALSE;
8668 /* check if element to clone is still there */
8669 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8671 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8679 /* cannot clone or target field not free anymore -- do not clone */
8680 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8681 Store[x][y] = EL_EMPTY;
8684 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8686 if (IS_MV_DIAGONAL(MovDir[x][y]))
8688 int diagonal_move_dir = MovDir[x][y];
8689 int stored = Store[x][y];
8690 int change_delay = 8;
8693 /* android is moving diagonally */
8695 CreateField(x, y, EL_DIAGONAL_SHRINKING);
8697 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8698 GfxElement[x][y] = EL_EMC_ANDROID;
8699 GfxAction[x][y] = ACTION_SHRINKING;
8700 GfxDir[x][y] = diagonal_move_dir;
8701 ChangeDelay[x][y] = change_delay;
8703 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8706 DrawLevelGraphicAnimation(x, y, graphic);
8707 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8709 if (Feld[newx][newy] == EL_ACID)
8711 SplashAcid(newx, newy);
8716 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8718 Store[newx][newy] = EL_EMC_ANDROID;
8719 GfxElement[newx][newy] = EL_EMC_ANDROID;
8720 GfxAction[newx][newy] = ACTION_GROWING;
8721 GfxDir[newx][newy] = diagonal_move_dir;
8722 ChangeDelay[newx][newy] = change_delay;
8724 graphic = el_act_dir2img(GfxElement[newx][newy],
8725 GfxAction[newx][newy], GfxDir[newx][newy]);
8727 DrawLevelGraphicAnimation(newx, newy, graphic);
8728 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8734 Feld[newx][newy] = EL_EMPTY;
8735 TEST_DrawLevelField(newx, newy);
8737 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8740 else if (!IS_FREE(newx, newy))
8743 if (IS_PLAYER(x, y))
8744 DrawPlayerField(x, y);
8746 TEST_DrawLevelField(x, y);
8752 else if (IS_CUSTOM_ELEMENT(element) &&
8753 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8756 if (!DigFieldByCE(newx, newy, element))
8759 int new_element = Feld[newx][newy];
8761 if (!IS_FREE(newx, newy))
8763 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8764 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8767 /* no element can dig solid indestructible elements */
8768 if (IS_INDESTRUCTIBLE(new_element) &&
8769 !IS_DIGGABLE(new_element) &&
8770 !IS_COLLECTIBLE(new_element))
8773 if (AmoebaNr[newx][newy] &&
8774 (new_element == EL_AMOEBA_FULL ||
8775 new_element == EL_BD_AMOEBA ||
8776 new_element == EL_AMOEBA_GROWING))
8778 AmoebaCnt[AmoebaNr[newx][newy]]--;
8779 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8782 if (IS_MOVING(newx, newy))
8783 RemoveMovingField(newx, newy);
8786 RemoveField(newx, newy);
8787 TEST_DrawLevelField(newx, newy);
8790 /* if digged element was about to explode, prevent the explosion */
8791 ExplodeField[newx][newy] = EX_TYPE_NONE;
8793 PlayLevelSoundAction(x, y, action);
8796 Store[newx][newy] = EL_EMPTY;
8799 /* this makes it possible to leave the removed element again */
8800 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8801 Store[newx][newy] = new_element;
8803 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8805 int move_leave_element = element_info[element].move_leave_element;
8807 /* this makes it possible to leave the removed element again */
8808 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8809 new_element : move_leave_element);
8815 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8817 RunnerVisit[x][y] = FrameCounter;
8818 PlayerVisit[x][y] /= 8; /* expire player visit path */
8821 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8823 if (!IS_FREE(newx, newy))
8825 if (IS_PLAYER(x, y))
8826 DrawPlayerField(x, y);
8828 TEST_DrawLevelField(x, y);
8834 boolean wanna_flame = !RND(10);
8835 int dx = newx - x, dy = newy - y;
8836 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8837 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8838 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8839 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8840 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8841 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8844 IS_CLASSIC_ENEMY(element1) ||
8845 IS_CLASSIC_ENEMY(element2)) &&
8846 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8847 element1 != EL_FLAMES && element2 != EL_FLAMES)
8849 ResetGfxAnimation(x, y);
8850 GfxAction[x][y] = ACTION_ATTACKING;
8852 if (IS_PLAYER(x, y))
8853 DrawPlayerField(x, y);
8855 TEST_DrawLevelField(x, y);
8857 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8859 MovDelay[x][y] = 50;
8863 RemoveField(newx, newy);
8865 Feld[newx][newy] = EL_FLAMES;
8866 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8869 RemoveField(newx1, newy1);
8871 Feld[newx1][newy1] = EL_FLAMES;
8873 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8876 RemoveField(newx2, newy2);
8878 Feld[newx2][newy2] = EL_FLAMES;
8885 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8886 Feld[newx][newy] == EL_DIAMOND)
8888 if (IS_MOVING(newx, newy))
8889 RemoveMovingField(newx, newy);
8892 Feld[newx][newy] = EL_EMPTY;
8893 TEST_DrawLevelField(newx, newy);
8896 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8898 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8899 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8901 if (AmoebaNr[newx][newy])
8903 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8904 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8905 Feld[newx][newy] == EL_BD_AMOEBA)
8906 AmoebaCnt[AmoebaNr[newx][newy]]--;
8911 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8913 RemoveMovingField(newx, newy);
8916 if (IS_MOVING(newx, newy))
8918 RemoveMovingField(newx, newy);
8923 Feld[newx][newy] = EL_EMPTY;
8924 TEST_DrawLevelField(newx, newy);
8927 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8929 else if ((element == EL_PACMAN || element == EL_MOLE)
8930 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8932 if (AmoebaNr[newx][newy])
8934 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8935 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8936 Feld[newx][newy] == EL_BD_AMOEBA)
8937 AmoebaCnt[AmoebaNr[newx][newy]]--;
8940 if (element == EL_MOLE)
8942 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8943 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8945 ResetGfxAnimation(x, y);
8946 GfxAction[x][y] = ACTION_DIGGING;
8947 TEST_DrawLevelField(x, y);
8949 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8951 return; /* wait for shrinking amoeba */
8953 else /* element == EL_PACMAN */
8955 Feld[newx][newy] = EL_EMPTY;
8956 TEST_DrawLevelField(newx, newy);
8957 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8960 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8961 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8962 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8964 /* wait for shrinking amoeba to completely disappear */
8967 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8969 /* object was running against a wall */
8974 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8975 if (move_pattern & MV_ANY_DIRECTION &&
8976 move_pattern == MovDir[x][y])
8978 int blocking_element =
8979 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8981 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8984 element = Feld[x][y]; /* element might have changed */
8988 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8989 DrawLevelElementAnimation(x, y, element);
8991 if (DONT_TOUCH(element))
8992 TestIfBadThingTouchesPlayer(x, y);
8997 InitMovingField(x, y, MovDir[x][y]);
8999 PlayLevelSoundAction(x, y, ACTION_MOVING);
9003 ContinueMoving(x, y);
9006 void ContinueMoving(int x, int y)
9008 int element = Feld[x][y];
9009 struct ElementInfo *ei = &element_info[element];
9010 int direction = MovDir[x][y];
9011 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9012 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9013 int newx = x + dx, newy = y + dy;
9014 int stored = Store[x][y];
9015 int stored_new = Store[newx][newy];
9016 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
9017 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
9018 boolean last_line = (newy == lev_fieldy - 1);
9020 MovPos[x][y] += getElementMoveStepsize(x, y);
9022 if (pushed_by_player) /* special case: moving object pushed by player */
9023 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
9025 if (ABS(MovPos[x][y]) < TILEX)
9028 int ee = Feld[x][y];
9029 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9030 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
9032 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
9033 x, y, ABS(MovPos[x][y]),
9035 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
9038 TEST_DrawLevelField(x, y);
9040 return; /* element is still moving */
9043 /* element reached destination field */
9045 Feld[x][y] = EL_EMPTY;
9046 Feld[newx][newy] = element;
9047 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
9049 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
9051 element = Feld[newx][newy] = EL_ACID;
9053 else if (element == EL_MOLE)
9055 Feld[x][y] = EL_SAND;
9057 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9059 else if (element == EL_QUICKSAND_FILLING)
9061 element = Feld[newx][newy] = get_next_element(element);
9062 Store[newx][newy] = Store[x][y];
9064 else if (element == EL_QUICKSAND_EMPTYING)
9066 Feld[x][y] = get_next_element(element);
9067 element = Feld[newx][newy] = Store[x][y];
9069 else if (element == EL_QUICKSAND_FAST_FILLING)
9071 element = Feld[newx][newy] = get_next_element(element);
9072 Store[newx][newy] = Store[x][y];
9074 else if (element == EL_QUICKSAND_FAST_EMPTYING)
9076 Feld[x][y] = get_next_element(element);
9077 element = Feld[newx][newy] = Store[x][y];
9079 else if (element == EL_MAGIC_WALL_FILLING)
9081 element = Feld[newx][newy] = get_next_element(element);
9082 if (!game.magic_wall_active)
9083 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
9084 Store[newx][newy] = Store[x][y];
9086 else if (element == EL_MAGIC_WALL_EMPTYING)
9088 Feld[x][y] = get_next_element(element);
9089 if (!game.magic_wall_active)
9090 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9091 element = Feld[newx][newy] = Store[x][y];
9093 #if USE_NEW_CUSTOM_VALUE
9094 InitField(newx, newy, FALSE);
9097 else if (element == EL_BD_MAGIC_WALL_FILLING)
9099 element = Feld[newx][newy] = get_next_element(element);
9100 if (!game.magic_wall_active)
9101 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
9102 Store[newx][newy] = Store[x][y];
9104 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
9106 Feld[x][y] = get_next_element(element);
9107 if (!game.magic_wall_active)
9108 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9109 element = Feld[newx][newy] = Store[x][y];
9111 #if USE_NEW_CUSTOM_VALUE
9112 InitField(newx, newy, FALSE);
9115 else if (element == EL_DC_MAGIC_WALL_FILLING)
9117 element = Feld[newx][newy] = get_next_element(element);
9118 if (!game.magic_wall_active)
9119 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
9120 Store[newx][newy] = Store[x][y];
9122 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
9124 Feld[x][y] = get_next_element(element);
9125 if (!game.magic_wall_active)
9126 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
9127 element = Feld[newx][newy] = Store[x][y];
9129 #if USE_NEW_CUSTOM_VALUE
9130 InitField(newx, newy, FALSE);
9133 else if (element == EL_AMOEBA_DROPPING)
9135 Feld[x][y] = get_next_element(element);
9136 element = Feld[newx][newy] = Store[x][y];
9138 else if (element == EL_SOKOBAN_OBJECT)
9141 Feld[x][y] = Back[x][y];
9143 if (Back[newx][newy])
9144 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
9146 Back[x][y] = Back[newx][newy] = 0;
9149 Store[x][y] = EL_EMPTY;
9154 MovDelay[newx][newy] = 0;
9156 if (CAN_CHANGE_OR_HAS_ACTION(element))
9158 /* copy element change control values to new field */
9159 ChangeDelay[newx][newy] = ChangeDelay[x][y];
9160 ChangePage[newx][newy] = ChangePage[x][y];
9161 ChangeCount[newx][newy] = ChangeCount[x][y];
9162 ChangeEvent[newx][newy] = ChangeEvent[x][y];
9165 #if USE_NEW_CUSTOM_VALUE
9166 CustomValue[newx][newy] = CustomValue[x][y];
9169 ChangeDelay[x][y] = 0;
9170 ChangePage[x][y] = -1;
9171 ChangeCount[x][y] = 0;
9172 ChangeEvent[x][y] = -1;
9174 #if USE_NEW_CUSTOM_VALUE
9175 CustomValue[x][y] = 0;
9178 /* copy animation control values to new field */
9179 GfxFrame[newx][newy] = GfxFrame[x][y];
9180 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
9181 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
9182 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
9184 Pushed[x][y] = Pushed[newx][newy] = FALSE;
9186 /* some elements can leave other elements behind after moving */
9188 if (ei->move_leave_element != EL_EMPTY &&
9189 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9190 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9192 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
9193 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9194 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9197 int move_leave_element = ei->move_leave_element;
9201 /* this makes it possible to leave the removed element again */
9202 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9203 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9205 /* this makes it possible to leave the removed element again */
9206 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9207 move_leave_element = stored;
9210 /* this makes it possible to leave the removed element again */
9211 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
9212 ei->move_leave_element == EL_TRIGGER_ELEMENT)
9213 move_leave_element = stored;
9216 Feld[x][y] = move_leave_element;
9218 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
9219 MovDir[x][y] = direction;
9221 InitField(x, y, FALSE);
9223 if (GFX_CRUMBLED(Feld[x][y]))
9224 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9226 if (ELEM_IS_PLAYER(move_leave_element))
9227 RelocatePlayer(x, y, move_leave_element);
9230 /* do this after checking for left-behind element */
9231 ResetGfxAnimation(x, y); /* reset animation values for old field */
9233 if (!CAN_MOVE(element) ||
9234 (CAN_FALL(element) && direction == MV_DOWN &&
9235 (element == EL_SPRING ||
9236 element_info[element].move_pattern == MV_WHEN_PUSHED ||
9237 element_info[element].move_pattern == MV_WHEN_DROPPED)))
9238 GfxDir[x][y] = MovDir[newx][newy] = 0;
9240 TEST_DrawLevelField(x, y);
9241 TEST_DrawLevelField(newx, newy);
9243 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
9245 /* prevent pushed element from moving on in pushed direction */
9246 if (pushed_by_player && CAN_MOVE(element) &&
9247 element_info[element].move_pattern & MV_ANY_DIRECTION &&
9248 !(element_info[element].move_pattern & direction))
9249 TurnRound(newx, newy);
9251 /* prevent elements on conveyor belt from moving on in last direction */
9252 if (pushed_by_conveyor && CAN_FALL(element) &&
9253 direction & MV_HORIZONTAL)
9254 MovDir[newx][newy] = 0;
9256 if (!pushed_by_player)
9258 int nextx = newx + dx, nexty = newy + dy;
9259 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9261 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9263 if (CAN_FALL(element) && direction == MV_DOWN)
9264 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9266 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9267 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9269 #if USE_FIX_IMPACT_COLLISION
9270 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9271 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9275 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
9277 TestIfBadThingTouchesPlayer(newx, newy);
9278 TestIfBadThingTouchesFriend(newx, newy);
9280 if (!IS_CUSTOM_ELEMENT(element))
9281 TestIfBadThingTouchesOtherBadThing(newx, newy);
9283 else if (element == EL_PENGUIN)
9284 TestIfFriendTouchesBadThing(newx, newy);
9286 if (DONT_GET_HIT_BY(element))
9288 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9291 /* give the player one last chance (one more frame) to move away */
9292 if (CAN_FALL(element) && direction == MV_DOWN &&
9293 (last_line || (!IS_FREE(x, newy + 1) &&
9294 (!IS_PLAYER(x, newy + 1) ||
9295 game.engine_version < VERSION_IDENT(3,1,1,0)))))
9298 if (pushed_by_player && !game.use_change_when_pushing_bug)
9300 int push_side = MV_DIR_OPPOSITE(direction);
9301 struct PlayerInfo *player = PLAYERINFO(x, y);
9303 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9304 player->index_bit, push_side);
9305 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9306 player->index_bit, push_side);
9309 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
9310 MovDelay[newx][newy] = 1;
9312 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9314 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
9317 if (ChangePage[newx][newy] != -1) /* delayed change */
9319 int page = ChangePage[newx][newy];
9320 struct ElementChangeInfo *change = &ei->change_page[page];
9322 ChangePage[newx][newy] = -1;
9324 if (change->can_change)
9326 if (ChangeElement(newx, newy, element, page))
9328 if (change->post_change_function)
9329 change->post_change_function(newx, newy);
9333 if (change->has_action)
9334 ExecuteCustomElementAction(newx, newy, element, page);
9338 TestIfElementHitsCustomElement(newx, newy, direction);
9339 TestIfPlayerTouchesCustomElement(newx, newy);
9340 TestIfElementTouchesCustomElement(newx, newy);
9342 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9343 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9344 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9345 MV_DIR_OPPOSITE(direction));
9348 int AmoebeNachbarNr(int ax, int ay)
9351 int element = Feld[ax][ay];
9353 static int xy[4][2] =
9361 for (i = 0; i < NUM_DIRECTIONS; i++)
9363 int x = ax + xy[i][0];
9364 int y = ay + xy[i][1];
9366 if (!IN_LEV_FIELD(x, y))
9369 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9370 group_nr = AmoebaNr[x][y];
9376 void AmoebenVereinigen(int ax, int ay)
9378 int i, x, y, xx, yy;
9379 int new_group_nr = AmoebaNr[ax][ay];
9380 static int xy[4][2] =
9388 if (new_group_nr == 0)
9391 for (i = 0; i < NUM_DIRECTIONS; i++)
9396 if (!IN_LEV_FIELD(x, y))
9399 if ((Feld[x][y] == EL_AMOEBA_FULL ||
9400 Feld[x][y] == EL_BD_AMOEBA ||
9401 Feld[x][y] == EL_AMOEBA_DEAD) &&
9402 AmoebaNr[x][y] != new_group_nr)
9404 int old_group_nr = AmoebaNr[x][y];
9406 if (old_group_nr == 0)
9409 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9410 AmoebaCnt[old_group_nr] = 0;
9411 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9412 AmoebaCnt2[old_group_nr] = 0;
9414 SCAN_PLAYFIELD(xx, yy)
9416 if (AmoebaNr[xx][yy] == old_group_nr)
9417 AmoebaNr[xx][yy] = new_group_nr;
9423 void AmoebeUmwandeln(int ax, int ay)
9427 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9429 int group_nr = AmoebaNr[ax][ay];
9434 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9435 printf("AmoebeUmwandeln(): This should never happen!\n");
9440 SCAN_PLAYFIELD(x, y)
9442 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9445 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9449 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9450 SND_AMOEBA_TURNING_TO_GEM :
9451 SND_AMOEBA_TURNING_TO_ROCK));
9456 static int xy[4][2] =
9464 for (i = 0; i < NUM_DIRECTIONS; i++)
9469 if (!IN_LEV_FIELD(x, y))
9472 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9474 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9475 SND_AMOEBA_TURNING_TO_GEM :
9476 SND_AMOEBA_TURNING_TO_ROCK));
9483 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9486 int group_nr = AmoebaNr[ax][ay];
9487 boolean done = FALSE;
9492 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9493 printf("AmoebeUmwandelnBD(): This should never happen!\n");
9498 SCAN_PLAYFIELD(x, y)
9500 if (AmoebaNr[x][y] == group_nr &&
9501 (Feld[x][y] == EL_AMOEBA_DEAD ||
9502 Feld[x][y] == EL_BD_AMOEBA ||
9503 Feld[x][y] == EL_AMOEBA_GROWING))
9506 Feld[x][y] = new_element;
9507 InitField(x, y, FALSE);
9508 TEST_DrawLevelField(x, y);
9514 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9515 SND_BD_AMOEBA_TURNING_TO_ROCK :
9516 SND_BD_AMOEBA_TURNING_TO_GEM));
9519 void AmoebeWaechst(int x, int y)
9521 static unsigned long sound_delay = 0;
9522 static unsigned long sound_delay_value = 0;
9524 if (!MovDelay[x][y]) /* start new growing cycle */
9528 if (DelayReached(&sound_delay, sound_delay_value))
9530 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9531 sound_delay_value = 30;
9535 if (MovDelay[x][y]) /* wait some time before growing bigger */
9538 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9540 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9541 6 - MovDelay[x][y]);
9543 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9546 if (!MovDelay[x][y])
9548 Feld[x][y] = Store[x][y];
9550 TEST_DrawLevelField(x, y);
9555 void AmoebaDisappearing(int x, int y)
9557 static unsigned long sound_delay = 0;
9558 static unsigned long sound_delay_value = 0;
9560 if (!MovDelay[x][y]) /* start new shrinking cycle */
9564 if (DelayReached(&sound_delay, sound_delay_value))
9565 sound_delay_value = 30;
9568 if (MovDelay[x][y]) /* wait some time before shrinking */
9571 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9573 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9574 6 - MovDelay[x][y]);
9576 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9579 if (!MovDelay[x][y])
9581 Feld[x][y] = EL_EMPTY;
9582 TEST_DrawLevelField(x, y);
9584 /* don't let mole enter this field in this cycle;
9585 (give priority to objects falling to this field from above) */
9591 void AmoebeAbleger(int ax, int ay)
9594 int element = Feld[ax][ay];
9595 int graphic = el2img(element);
9596 int newax = ax, neway = ay;
9597 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9598 static int xy[4][2] =
9606 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9608 Feld[ax][ay] = EL_AMOEBA_DEAD;
9609 TEST_DrawLevelField(ax, ay);
9613 if (IS_ANIMATED(graphic))
9614 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9616 if (!MovDelay[ax][ay]) /* start making new amoeba field */
9617 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9619 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
9622 if (MovDelay[ax][ay])
9626 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9629 int x = ax + xy[start][0];
9630 int y = ay + xy[start][1];
9632 if (!IN_LEV_FIELD(x, y))
9635 if (IS_FREE(x, y) ||
9636 CAN_GROW_INTO(Feld[x][y]) ||
9637 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9638 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9644 if (newax == ax && neway == ay)
9647 else /* normal or "filled" (BD style) amoeba */
9650 boolean waiting_for_player = FALSE;
9652 for (i = 0; i < NUM_DIRECTIONS; i++)
9654 int j = (start + i) % 4;
9655 int x = ax + xy[j][0];
9656 int y = ay + xy[j][1];
9658 if (!IN_LEV_FIELD(x, y))
9661 if (IS_FREE(x, y) ||
9662 CAN_GROW_INTO(Feld[x][y]) ||
9663 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9664 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9670 else if (IS_PLAYER(x, y))
9671 waiting_for_player = TRUE;
9674 if (newax == ax && neway == ay) /* amoeba cannot grow */
9676 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9678 Feld[ax][ay] = EL_AMOEBA_DEAD;
9679 TEST_DrawLevelField(ax, ay);
9680 AmoebaCnt[AmoebaNr[ax][ay]]--;
9682 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
9684 if (element == EL_AMOEBA_FULL)
9685 AmoebeUmwandeln(ax, ay);
9686 else if (element == EL_BD_AMOEBA)
9687 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9692 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9694 /* amoeba gets larger by growing in some direction */
9696 int new_group_nr = AmoebaNr[ax][ay];
9699 if (new_group_nr == 0)
9701 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9702 printf("AmoebeAbleger(): This should never happen!\n");
9707 AmoebaNr[newax][neway] = new_group_nr;
9708 AmoebaCnt[new_group_nr]++;
9709 AmoebaCnt2[new_group_nr]++;
9711 /* if amoeba touches other amoeba(s) after growing, unify them */
9712 AmoebenVereinigen(newax, neway);
9714 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9716 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9722 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9723 (neway == lev_fieldy - 1 && newax != ax))
9725 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
9726 Store[newax][neway] = element;
9728 else if (neway == ay || element == EL_EMC_DRIPPER)
9730 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
9732 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9736 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
9737 Feld[ax][ay] = EL_AMOEBA_DROPPING;
9738 Store[ax][ay] = EL_AMOEBA_DROP;
9739 ContinueMoving(ax, ay);
9743 TEST_DrawLevelField(newax, neway);
9746 void Life(int ax, int ay)
9750 int element = Feld[ax][ay];
9751 int graphic = el2img(element);
9752 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9754 boolean changed = FALSE;
9756 if (IS_ANIMATED(graphic))
9757 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9762 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
9763 MovDelay[ax][ay] = life_time;
9765 if (MovDelay[ax][ay]) /* wait some time before next cycle */
9768 if (MovDelay[ax][ay])
9772 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9774 int xx = ax+x1, yy = ay+y1;
9777 if (!IN_LEV_FIELD(xx, yy))
9780 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9782 int x = xx+x2, y = yy+y2;
9784 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9787 if (((Feld[x][y] == element ||
9788 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9790 (IS_FREE(x, y) && Stop[x][y]))
9794 if (xx == ax && yy == ay) /* field in the middle */
9796 if (nachbarn < life_parameter[0] ||
9797 nachbarn > life_parameter[1])
9799 Feld[xx][yy] = EL_EMPTY;
9801 TEST_DrawLevelField(xx, yy);
9802 Stop[xx][yy] = TRUE;
9806 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9807 { /* free border field */
9808 if (nachbarn >= life_parameter[2] &&
9809 nachbarn <= life_parameter[3])
9811 Feld[xx][yy] = element;
9812 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9814 TEST_DrawLevelField(xx, yy);
9815 Stop[xx][yy] = TRUE;
9822 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9823 SND_GAME_OF_LIFE_GROWING);
9826 static void InitRobotWheel(int x, int y)
9828 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9831 static void RunRobotWheel(int x, int y)
9833 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9836 static void StopRobotWheel(int x, int y)
9838 if (ZX == x && ZY == y)
9842 game.robot_wheel_active = FALSE;
9846 static void InitTimegateWheel(int x, int y)
9848 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9851 static void RunTimegateWheel(int x, int y)
9853 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9856 static void InitMagicBallDelay(int x, int y)
9859 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9861 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9865 static void ActivateMagicBall(int bx, int by)
9869 if (level.ball_random)
9871 int pos_border = RND(8); /* select one of the eight border elements */
9872 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9873 int xx = pos_content % 3;
9874 int yy = pos_content / 3;
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 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9886 int xx = x - bx + 1;
9887 int yy = y - by + 1;
9889 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9890 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9894 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9897 void CheckExit(int x, int y)
9899 if (local_player->gems_still_needed > 0 ||
9900 local_player->sokobanfields_still_needed > 0 ||
9901 local_player->lights_still_needed > 0)
9903 int element = Feld[x][y];
9904 int graphic = el2img(element);
9906 if (IS_ANIMATED(graphic))
9907 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9912 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9915 Feld[x][y] = EL_EXIT_OPENING;
9917 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9920 void CheckExitEM(int x, int y)
9922 if (local_player->gems_still_needed > 0 ||
9923 local_player->sokobanfields_still_needed > 0 ||
9924 local_player->lights_still_needed > 0)
9926 int element = Feld[x][y];
9927 int graphic = el2img(element);
9929 if (IS_ANIMATED(graphic))
9930 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9935 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9938 Feld[x][y] = EL_EM_EXIT_OPENING;
9940 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9943 void CheckExitSteel(int x, int y)
9945 if (local_player->gems_still_needed > 0 ||
9946 local_player->sokobanfields_still_needed > 0 ||
9947 local_player->lights_still_needed > 0)
9949 int element = Feld[x][y];
9950 int graphic = el2img(element);
9952 if (IS_ANIMATED(graphic))
9953 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9958 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9961 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9963 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9966 void CheckExitSteelEM(int x, int y)
9968 if (local_player->gems_still_needed > 0 ||
9969 local_player->sokobanfields_still_needed > 0 ||
9970 local_player->lights_still_needed > 0)
9972 int element = Feld[x][y];
9973 int graphic = el2img(element);
9975 if (IS_ANIMATED(graphic))
9976 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9981 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9984 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9986 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9989 void CheckExitSP(int x, int y)
9991 if (local_player->gems_still_needed > 0)
9993 int element = Feld[x][y];
9994 int graphic = el2img(element);
9996 if (IS_ANIMATED(graphic))
9997 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10002 if (AllPlayersGone) /* do not re-open exit door closed after last player */
10005 Feld[x][y] = EL_SP_EXIT_OPENING;
10007 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
10010 static void CloseAllOpenTimegates()
10014 SCAN_PLAYFIELD(x, y)
10016 int element = Feld[x][y];
10018 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
10020 Feld[x][y] = EL_TIMEGATE_CLOSING;
10022 PlayLevelSoundAction(x, y, ACTION_CLOSING);
10027 void DrawTwinkleOnField(int x, int y)
10029 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
10032 if (Feld[x][y] == EL_BD_DIAMOND)
10035 if (MovDelay[x][y] == 0) /* next animation frame */
10036 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
10038 if (MovDelay[x][y] != 0) /* wait some time before next frame */
10042 DrawLevelElementAnimation(x, y, Feld[x][y]);
10044 if (MovDelay[x][y] != 0)
10046 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
10047 10 - MovDelay[x][y]);
10049 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
10054 void MauerWaechst(int x, int y)
10058 if (!MovDelay[x][y]) /* next animation frame */
10059 MovDelay[x][y] = 3 * delay;
10061 if (MovDelay[x][y]) /* wait some time before next frame */
10065 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
10067 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
10068 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
10070 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
10073 if (!MovDelay[x][y])
10075 if (MovDir[x][y] == MV_LEFT)
10077 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
10078 TEST_DrawLevelField(x - 1, y);
10080 else if (MovDir[x][y] == MV_RIGHT)
10082 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
10083 TEST_DrawLevelField(x + 1, y);
10085 else if (MovDir[x][y] == MV_UP)
10087 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
10088 TEST_DrawLevelField(x, y - 1);
10092 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
10093 TEST_DrawLevelField(x, y + 1);
10096 Feld[x][y] = Store[x][y];
10098 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
10099 TEST_DrawLevelField(x, y);
10104 void MauerAbleger(int ax, int ay)
10106 int element = Feld[ax][ay];
10107 int graphic = el2img(element);
10108 boolean oben_frei = FALSE, unten_frei = FALSE;
10109 boolean links_frei = FALSE, rechts_frei = FALSE;
10110 boolean oben_massiv = FALSE, unten_massiv = FALSE;
10111 boolean links_massiv = FALSE, rechts_massiv = FALSE;
10112 boolean new_wall = FALSE;
10114 if (IS_ANIMATED(graphic))
10115 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10117 if (!MovDelay[ax][ay]) /* start building new wall */
10118 MovDelay[ax][ay] = 6;
10120 if (MovDelay[ax][ay]) /* wait some time before building new wall */
10122 MovDelay[ax][ay]--;
10123 if (MovDelay[ax][ay])
10127 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10129 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10131 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10133 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10134 rechts_frei = TRUE;
10136 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
10137 element == EL_EXPANDABLE_WALL_ANY)
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_UP;
10144 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10145 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10146 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
10151 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
10152 Store[ax][ay+1] = element;
10153 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10154 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10155 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10156 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
10161 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10162 element == EL_EXPANDABLE_WALL_ANY ||
10163 element == EL_EXPANDABLE_WALL ||
10164 element == EL_BD_EXPANDABLE_WALL)
10168 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
10169 Store[ax-1][ay] = element;
10170 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10171 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10172 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10173 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
10179 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
10180 Store[ax+1][ay] = element;
10181 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10182 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10183 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10184 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
10189 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
10190 TEST_DrawLevelField(ax, ay);
10192 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10193 oben_massiv = TRUE;
10194 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10195 unten_massiv = TRUE;
10196 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10197 links_massiv = TRUE;
10198 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10199 rechts_massiv = TRUE;
10201 if (((oben_massiv && unten_massiv) ||
10202 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10203 element == EL_EXPANDABLE_WALL) &&
10204 ((links_massiv && rechts_massiv) ||
10205 element == EL_EXPANDABLE_WALL_VERTICAL))
10206 Feld[ax][ay] = EL_WALL;
10209 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10212 void MauerAblegerStahl(int ax, int ay)
10214 int element = Feld[ax][ay];
10215 int graphic = el2img(element);
10216 boolean oben_frei = FALSE, unten_frei = FALSE;
10217 boolean links_frei = FALSE, rechts_frei = FALSE;
10218 boolean oben_massiv = FALSE, unten_massiv = FALSE;
10219 boolean links_massiv = FALSE, rechts_massiv = FALSE;
10220 boolean new_wall = FALSE;
10222 if (IS_ANIMATED(graphic))
10223 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10225 if (!MovDelay[ax][ay]) /* start building new wall */
10226 MovDelay[ax][ay] = 6;
10228 if (MovDelay[ax][ay]) /* wait some time before building new wall */
10230 MovDelay[ax][ay]--;
10231 if (MovDelay[ax][ay])
10235 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10237 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10239 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10241 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10242 rechts_frei = TRUE;
10244 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10245 element == EL_EXPANDABLE_STEELWALL_ANY)
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_UP;
10252 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10253 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10254 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10259 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10260 Store[ax][ay+1] = element;
10261 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10262 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10263 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10264 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10269 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10270 element == EL_EXPANDABLE_STEELWALL_ANY)
10274 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10275 Store[ax-1][ay] = element;
10276 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10277 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10278 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10279 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10285 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10286 Store[ax+1][ay] = element;
10287 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10288 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10289 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10290 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10295 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10296 oben_massiv = TRUE;
10297 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10298 unten_massiv = TRUE;
10299 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10300 links_massiv = TRUE;
10301 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10302 rechts_massiv = TRUE;
10304 if (((oben_massiv && unten_massiv) ||
10305 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10306 ((links_massiv && rechts_massiv) ||
10307 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10308 Feld[ax][ay] = EL_STEELWALL;
10311 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10314 void CheckForDragon(int x, int y)
10317 boolean dragon_found = FALSE;
10318 static int xy[4][2] =
10326 for (i = 0; i < NUM_DIRECTIONS; i++)
10328 for (j = 0; j < 4; j++)
10330 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10332 if (IN_LEV_FIELD(xx, yy) &&
10333 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10335 if (Feld[xx][yy] == EL_DRAGON)
10336 dragon_found = TRUE;
10345 for (i = 0; i < NUM_DIRECTIONS; i++)
10347 for (j = 0; j < 3; j++)
10349 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10351 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10353 Feld[xx][yy] = EL_EMPTY;
10354 TEST_DrawLevelField(xx, yy);
10363 static void InitBuggyBase(int x, int y)
10365 int element = Feld[x][y];
10366 int activating_delay = FRAMES_PER_SECOND / 4;
10368 ChangeDelay[x][y] =
10369 (element == EL_SP_BUGGY_BASE ?
10370 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10371 element == EL_SP_BUGGY_BASE_ACTIVATING ?
10373 element == EL_SP_BUGGY_BASE_ACTIVE ?
10374 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10377 static void WarnBuggyBase(int x, int y)
10380 static int xy[4][2] =
10388 for (i = 0; i < NUM_DIRECTIONS; i++)
10390 int xx = x + xy[i][0];
10391 int yy = y + xy[i][1];
10393 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10395 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10402 static void InitTrap(int x, int y)
10404 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10407 static void ActivateTrap(int x, int y)
10409 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10412 static void ChangeActiveTrap(int x, int y)
10414 int graphic = IMG_TRAP_ACTIVE;
10416 /* if new animation frame was drawn, correct crumbled sand border */
10417 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10418 TEST_DrawLevelFieldCrumbled(x, y);
10421 static int getSpecialActionElement(int element, int number, int base_element)
10423 return (element != EL_EMPTY ? element :
10424 number != -1 ? base_element + number - 1 :
10428 static int getModifiedActionNumber(int value_old, int operator, int operand,
10429 int value_min, int value_max)
10431 int value_new = (operator == CA_MODE_SET ? operand :
10432 operator == CA_MODE_ADD ? value_old + operand :
10433 operator == CA_MODE_SUBTRACT ? value_old - operand :
10434 operator == CA_MODE_MULTIPLY ? value_old * operand :
10435 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
10436 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
10439 return (value_new < value_min ? value_min :
10440 value_new > value_max ? value_max :
10444 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10446 struct ElementInfo *ei = &element_info[element];
10447 struct ElementChangeInfo *change = &ei->change_page[page];
10448 int target_element = change->target_element;
10449 int action_type = change->action_type;
10450 int action_mode = change->action_mode;
10451 int action_arg = change->action_arg;
10452 int action_element = change->action_element;
10455 if (!change->has_action)
10458 /* ---------- determine action paramater values -------------------------- */
10460 int level_time_value =
10461 (level.time > 0 ? TimeLeft :
10464 int action_arg_element_raw =
10465 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
10466 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10467 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
10468 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
10469 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10470 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
10471 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
10473 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10476 if (action_arg_element_raw == EL_GROUP_START)
10477 printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10480 int action_arg_direction =
10481 (action_arg >= CA_ARG_DIRECTION_LEFT &&
10482 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10483 action_arg == CA_ARG_DIRECTION_TRIGGER ?
10484 change->actual_trigger_side :
10485 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10486 MV_DIR_OPPOSITE(change->actual_trigger_side) :
10489 int action_arg_number_min =
10490 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10493 int action_arg_number_max =
10494 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10495 action_type == CA_SET_LEVEL_GEMS ? 999 :
10496 action_type == CA_SET_LEVEL_TIME ? 9999 :
10497 action_type == CA_SET_LEVEL_SCORE ? 99999 :
10498 action_type == CA_SET_CE_VALUE ? 9999 :
10499 action_type == CA_SET_CE_SCORE ? 9999 :
10502 int action_arg_number_reset =
10503 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10504 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10505 action_type == CA_SET_LEVEL_TIME ? level.time :
10506 action_type == CA_SET_LEVEL_SCORE ? 0 :
10507 #if USE_NEW_CUSTOM_VALUE
10508 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10510 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10512 action_type == CA_SET_CE_SCORE ? 0 :
10515 int action_arg_number =
10516 (action_arg <= CA_ARG_MAX ? action_arg :
10517 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10518 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10519 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10520 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10521 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10522 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10523 #if USE_NEW_CUSTOM_VALUE
10524 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10526 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10528 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10529 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10530 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10531 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10532 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10533 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10534 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10535 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10536 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10537 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10538 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10539 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
10540 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10541 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
10544 int action_arg_number_old =
10545 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10546 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10547 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10548 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10549 action_type == CA_SET_CE_SCORE ? ei->collect_score :
10552 int action_arg_number_new =
10553 getModifiedActionNumber(action_arg_number_old,
10554 action_mode, action_arg_number,
10555 action_arg_number_min, action_arg_number_max);
10558 int trigger_player_bits =
10559 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10560 change->actual_trigger_player_bits : change->trigger_player);
10562 int trigger_player_bits =
10563 (change->actual_trigger_player >= EL_PLAYER_1 &&
10564 change->actual_trigger_player <= EL_PLAYER_4 ?
10565 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10569 int action_arg_player_bits =
10570 (action_arg >= CA_ARG_PLAYER_1 &&
10571 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10572 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10573 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10576 /* ---------- execute action -------------------------------------------- */
10578 switch (action_type)
10585 /* ---------- level actions ------------------------------------------- */
10587 case CA_RESTART_LEVEL:
10589 game.restart_level = TRUE;
10594 case CA_SHOW_ENVELOPE:
10596 int element = getSpecialActionElement(action_arg_element,
10597 action_arg_number, EL_ENVELOPE_1);
10599 if (IS_ENVELOPE(element))
10600 local_player->show_envelope = element;
10605 case CA_SET_LEVEL_TIME:
10607 if (level.time > 0) /* only modify limited time value */
10609 TimeLeft = action_arg_number_new;
10612 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10614 DisplayGameControlValues();
10616 DrawGameValue_Time(TimeLeft);
10619 if (!TimeLeft && setup.time_limit)
10620 for (i = 0; i < MAX_PLAYERS; i++)
10621 KillPlayer(&stored_player[i]);
10627 case CA_SET_LEVEL_SCORE:
10629 local_player->score = action_arg_number_new;
10632 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10634 DisplayGameControlValues();
10636 DrawGameValue_Score(local_player->score);
10642 case CA_SET_LEVEL_GEMS:
10644 local_player->gems_still_needed = action_arg_number_new;
10647 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10649 DisplayGameControlValues();
10651 DrawGameValue_Emeralds(local_player->gems_still_needed);
10657 #if !USE_PLAYER_GRAVITY
10658 case CA_SET_LEVEL_GRAVITY:
10660 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10661 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10662 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10668 case CA_SET_LEVEL_WIND:
10670 game.wind_direction = action_arg_direction;
10675 case CA_SET_LEVEL_RANDOM_SEED:
10678 /* ensure that setting a new random seed while playing is predictable */
10679 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10681 InitRND(action_arg_number_new);
10685 printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10693 for (i = 0; i < 9; i++)
10694 printf("%d, ", RND(2));
10702 /* ---------- player actions ------------------------------------------ */
10704 case CA_MOVE_PLAYER:
10706 /* automatically move to the next field in specified direction */
10707 for (i = 0; i < MAX_PLAYERS; i++)
10708 if (trigger_player_bits & (1 << i))
10709 stored_player[i].programmed_action = action_arg_direction;
10714 case CA_EXIT_PLAYER:
10716 for (i = 0; i < MAX_PLAYERS; i++)
10717 if (action_arg_player_bits & (1 << i))
10718 PlayerWins(&stored_player[i]);
10723 case CA_KILL_PLAYER:
10725 for (i = 0; i < MAX_PLAYERS; i++)
10726 if (action_arg_player_bits & (1 << i))
10727 KillPlayer(&stored_player[i]);
10732 case CA_SET_PLAYER_KEYS:
10734 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10735 int element = getSpecialActionElement(action_arg_element,
10736 action_arg_number, EL_KEY_1);
10738 if (IS_KEY(element))
10740 for (i = 0; i < MAX_PLAYERS; i++)
10742 if (trigger_player_bits & (1 << i))
10744 stored_player[i].key[KEY_NR(element)] = key_state;
10746 DrawGameDoorValues();
10754 case CA_SET_PLAYER_SPEED:
10757 printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10760 for (i = 0; i < MAX_PLAYERS; i++)
10762 if (trigger_player_bits & (1 << i))
10764 int move_stepsize = TILEX / stored_player[i].move_delay_value;
10766 if (action_arg == CA_ARG_SPEED_FASTER &&
10767 stored_player[i].cannot_move)
10769 action_arg_number = STEPSIZE_VERY_SLOW;
10771 else if (action_arg == CA_ARG_SPEED_SLOWER ||
10772 action_arg == CA_ARG_SPEED_FASTER)
10774 action_arg_number = 2;
10775 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10778 else if (action_arg == CA_ARG_NUMBER_RESET)
10780 action_arg_number = level.initial_player_stepsize[i];
10784 getModifiedActionNumber(move_stepsize,
10787 action_arg_number_min,
10788 action_arg_number_max);
10790 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10797 case CA_SET_PLAYER_SHIELD:
10799 for (i = 0; i < MAX_PLAYERS; i++)
10801 if (trigger_player_bits & (1 << i))
10803 if (action_arg == CA_ARG_SHIELD_OFF)
10805 stored_player[i].shield_normal_time_left = 0;
10806 stored_player[i].shield_deadly_time_left = 0;
10808 else if (action_arg == CA_ARG_SHIELD_NORMAL)
10810 stored_player[i].shield_normal_time_left = 999999;
10812 else if (action_arg == CA_ARG_SHIELD_DEADLY)
10814 stored_player[i].shield_normal_time_left = 999999;
10815 stored_player[i].shield_deadly_time_left = 999999;
10823 #if USE_PLAYER_GRAVITY
10824 case CA_SET_PLAYER_GRAVITY:
10826 for (i = 0; i < MAX_PLAYERS; i++)
10828 if (trigger_player_bits & (1 << i))
10830 stored_player[i].gravity =
10831 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10832 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10833 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10834 stored_player[i].gravity);
10842 case CA_SET_PLAYER_ARTWORK:
10844 for (i = 0; i < MAX_PLAYERS; i++)
10846 if (trigger_player_bits & (1 << i))
10848 int artwork_element = action_arg_element;
10850 if (action_arg == CA_ARG_ELEMENT_RESET)
10852 (level.use_artwork_element[i] ? level.artwork_element[i] :
10853 stored_player[i].element_nr);
10855 #if USE_GFX_RESET_PLAYER_ARTWORK
10856 if (stored_player[i].artwork_element != artwork_element)
10857 stored_player[i].Frame = 0;
10860 stored_player[i].artwork_element = artwork_element;
10862 SetPlayerWaiting(&stored_player[i], FALSE);
10864 /* set number of special actions for bored and sleeping animation */
10865 stored_player[i].num_special_action_bored =
10866 get_num_special_action(artwork_element,
10867 ACTION_BORING_1, ACTION_BORING_LAST);
10868 stored_player[i].num_special_action_sleeping =
10869 get_num_special_action(artwork_element,
10870 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10877 case CA_SET_PLAYER_INVENTORY:
10879 for (i = 0; i < MAX_PLAYERS; i++)
10881 struct PlayerInfo *player = &stored_player[i];
10884 if (trigger_player_bits & (1 << i))
10886 int inventory_element = action_arg_element;
10888 if (action_arg == CA_ARG_ELEMENT_TARGET ||
10889 action_arg == CA_ARG_ELEMENT_TRIGGER ||
10890 action_arg == CA_ARG_ELEMENT_ACTION)
10892 int element = inventory_element;
10893 int collect_count = element_info[element].collect_count_initial;
10895 if (!IS_CUSTOM_ELEMENT(element))
10898 if (collect_count == 0)
10899 player->inventory_infinite_element = element;
10901 for (k = 0; k < collect_count; k++)
10902 if (player->inventory_size < MAX_INVENTORY_SIZE)
10903 player->inventory_element[player->inventory_size++] =
10906 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10907 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10908 action_arg == CA_ARG_INVENTORY_RM_ACTION)
10910 if (player->inventory_infinite_element != EL_UNDEFINED &&
10911 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10912 action_arg_element_raw))
10913 player->inventory_infinite_element = EL_UNDEFINED;
10915 for (k = 0, j = 0; j < player->inventory_size; j++)
10917 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10918 action_arg_element_raw))
10919 player->inventory_element[k++] = player->inventory_element[j];
10922 player->inventory_size = k;
10924 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10926 if (player->inventory_size > 0)
10928 for (j = 0; j < player->inventory_size - 1; j++)
10929 player->inventory_element[j] = player->inventory_element[j + 1];
10931 player->inventory_size--;
10934 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10936 if (player->inventory_size > 0)
10937 player->inventory_size--;
10939 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10941 player->inventory_infinite_element = EL_UNDEFINED;
10942 player->inventory_size = 0;
10944 else if (action_arg == CA_ARG_INVENTORY_RESET)
10946 player->inventory_infinite_element = EL_UNDEFINED;
10947 player->inventory_size = 0;
10949 if (level.use_initial_inventory[i])
10951 for (j = 0; j < level.initial_inventory_size[i]; j++)
10953 int element = level.initial_inventory_content[i][j];
10954 int collect_count = element_info[element].collect_count_initial;
10956 if (!IS_CUSTOM_ELEMENT(element))
10959 if (collect_count == 0)
10960 player->inventory_infinite_element = element;
10962 for (k = 0; k < collect_count; k++)
10963 if (player->inventory_size < MAX_INVENTORY_SIZE)
10964 player->inventory_element[player->inventory_size++] =
10975 /* ---------- CE actions ---------------------------------------------- */
10977 case CA_SET_CE_VALUE:
10979 #if USE_NEW_CUSTOM_VALUE
10980 int last_ce_value = CustomValue[x][y];
10982 CustomValue[x][y] = action_arg_number_new;
10984 if (CustomValue[x][y] != last_ce_value)
10986 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10987 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10989 if (CustomValue[x][y] == 0)
10991 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10992 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
11000 case CA_SET_CE_SCORE:
11002 #if USE_NEW_CUSTOM_VALUE
11003 int last_ce_score = ei->collect_score;
11005 ei->collect_score = action_arg_number_new;
11007 if (ei->collect_score != last_ce_score)
11009 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
11010 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
11012 if (ei->collect_score == 0)
11016 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
11017 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
11020 This is a very special case that seems to be a mixture between
11021 CheckElementChange() and CheckTriggeredElementChange(): while
11022 the first one only affects single elements that are triggered
11023 directly, the second one affects multiple elements in the playfield
11024 that are triggered indirectly by another element. This is a third
11025 case: Changing the CE score always affects multiple identical CEs,
11026 so every affected CE must be checked, not only the single CE for
11027 which the CE score was changed in the first place (as every instance
11028 of that CE shares the same CE score, and therefore also can change)!
11030 SCAN_PLAYFIELD(xx, yy)
11032 if (Feld[xx][yy] == element)
11033 CheckElementChange(xx, yy, element, EL_UNDEFINED,
11034 CE_SCORE_GETS_ZERO);
11043 case CA_SET_CE_ARTWORK:
11045 int artwork_element = action_arg_element;
11046 boolean reset_frame = FALSE;
11049 if (action_arg == CA_ARG_ELEMENT_RESET)
11050 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
11053 if (ei->gfx_element != artwork_element)
11054 reset_frame = TRUE;
11056 ei->gfx_element = artwork_element;
11058 SCAN_PLAYFIELD(xx, yy)
11060 if (Feld[xx][yy] == element)
11064 ResetGfxAnimation(xx, yy);
11065 ResetRandomAnimationValue(xx, yy);
11068 TEST_DrawLevelField(xx, yy);
11075 /* ---------- engine actions ------------------------------------------ */
11077 case CA_SET_ENGINE_SCAN_MODE:
11079 InitPlayfieldScanMode(action_arg);
11089 static void CreateFieldExt(int x, int y, int element, boolean is_change)
11091 int old_element = Feld[x][y];
11092 int new_element = GetElementFromGroupElement(element);
11093 int previous_move_direction = MovDir[x][y];
11094 #if USE_NEW_CUSTOM_VALUE
11095 int last_ce_value = CustomValue[x][y];
11097 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
11098 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
11099 boolean add_player_onto_element = (new_element_is_player &&
11100 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
11101 /* this breaks SnakeBite when a snake is
11102 halfway through a door that closes */
11103 /* NOW FIXED AT LEVEL INIT IN files.c */
11104 new_element != EL_SOKOBAN_FIELD_PLAYER &&
11106 IS_WALKABLE(old_element));
11109 /* check if element under the player changes from accessible to unaccessible
11110 (needed for special case of dropping element which then changes) */
11111 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11112 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11120 if (!add_player_onto_element)
11122 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
11123 RemoveMovingField(x, y);
11127 Feld[x][y] = new_element;
11129 #if !USE_GFX_RESET_GFX_ANIMATION
11130 ResetGfxAnimation(x, y);
11131 ResetRandomAnimationValue(x, y);
11134 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
11135 MovDir[x][y] = previous_move_direction;
11137 #if USE_NEW_CUSTOM_VALUE
11138 if (element_info[new_element].use_last_ce_value)
11139 CustomValue[x][y] = last_ce_value;
11142 InitField_WithBug1(x, y, FALSE);
11144 new_element = Feld[x][y]; /* element may have changed */
11146 #if USE_GFX_RESET_GFX_ANIMATION
11147 ResetGfxAnimation(x, y);
11148 ResetRandomAnimationValue(x, y);
11151 TEST_DrawLevelField(x, y);
11153 if (GFX_CRUMBLED(new_element))
11154 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
11158 /* check if element under the player changes from accessible to unaccessible
11159 (needed for special case of dropping element which then changes) */
11160 /* (must be checked after creating new element for walkable group elements) */
11161 #if USE_FIX_KILLED_BY_NON_WALKABLE
11162 if (IS_PLAYER(x, y) && !player_explosion_protected &&
11163 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11170 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11171 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11180 /* "ChangeCount" not set yet to allow "entered by player" change one time */
11181 if (new_element_is_player)
11182 RelocatePlayer(x, y, new_element);
11185 ChangeCount[x][y]++; /* count number of changes in the same frame */
11187 TestIfBadThingTouchesPlayer(x, y);
11188 TestIfPlayerTouchesCustomElement(x, y);
11189 TestIfElementTouchesCustomElement(x, y);
11192 static void CreateField(int x, int y, int element)
11194 CreateFieldExt(x, y, element, FALSE);
11197 static void CreateElementFromChange(int x, int y, int element)
11199 element = GET_VALID_RUNTIME_ELEMENT(element);
11201 #if USE_STOP_CHANGED_ELEMENTS
11202 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11204 int old_element = Feld[x][y];
11206 /* prevent changed element from moving in same engine frame
11207 unless both old and new element can either fall or move */
11208 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
11209 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
11214 CreateFieldExt(x, y, element, TRUE);
11217 static boolean ChangeElement(int x, int y, int element, int page)
11219 struct ElementInfo *ei = &element_info[element];
11220 struct ElementChangeInfo *change = &ei->change_page[page];
11221 int ce_value = CustomValue[x][y];
11222 int ce_score = ei->collect_score;
11223 int target_element;
11224 int old_element = Feld[x][y];
11226 /* always use default change event to prevent running into a loop */
11227 if (ChangeEvent[x][y] == -1)
11228 ChangeEvent[x][y] = CE_DELAY;
11230 if (ChangeEvent[x][y] == CE_DELAY)
11232 /* reset actual trigger element, trigger player and action element */
11233 change->actual_trigger_element = EL_EMPTY;
11234 change->actual_trigger_player = EL_EMPTY;
11235 change->actual_trigger_player_bits = CH_PLAYER_NONE;
11236 change->actual_trigger_side = CH_SIDE_NONE;
11237 change->actual_trigger_ce_value = 0;
11238 change->actual_trigger_ce_score = 0;
11241 /* do not change elements more than a specified maximum number of changes */
11242 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11245 ChangeCount[x][y]++; /* count number of changes in the same frame */
11247 if (change->explode)
11254 if (change->use_target_content)
11256 boolean complete_replace = TRUE;
11257 boolean can_replace[3][3];
11260 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11263 boolean is_walkable;
11264 boolean is_diggable;
11265 boolean is_collectible;
11266 boolean is_removable;
11267 boolean is_destructible;
11268 int ex = x + xx - 1;
11269 int ey = y + yy - 1;
11270 int content_element = change->target_content.e[xx][yy];
11273 can_replace[xx][yy] = TRUE;
11275 if (ex == x && ey == y) /* do not check changing element itself */
11278 if (content_element == EL_EMPTY_SPACE)
11280 can_replace[xx][yy] = FALSE; /* do not replace border with space */
11285 if (!IN_LEV_FIELD(ex, ey))
11287 can_replace[xx][yy] = FALSE;
11288 complete_replace = FALSE;
11295 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11296 e = MovingOrBlocked2Element(ex, ey);
11298 is_empty = (IS_FREE(ex, ey) ||
11299 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11301 is_walkable = (is_empty || IS_WALKABLE(e));
11302 is_diggable = (is_empty || IS_DIGGABLE(e));
11303 is_collectible = (is_empty || IS_COLLECTIBLE(e));
11304 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11305 is_removable = (is_diggable || is_collectible);
11307 can_replace[xx][yy] =
11308 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
11309 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
11310 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
11311 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
11312 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
11313 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11314 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11316 if (!can_replace[xx][yy])
11317 complete_replace = FALSE;
11320 if (!change->only_if_complete || complete_replace)
11322 boolean something_has_changed = FALSE;
11324 if (change->only_if_complete && change->use_random_replace &&
11325 RND(100) < change->random_percentage)
11328 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11330 int ex = x + xx - 1;
11331 int ey = y + yy - 1;
11332 int content_element;
11334 if (can_replace[xx][yy] && (!change->use_random_replace ||
11335 RND(100) < change->random_percentage))
11337 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11338 RemoveMovingField(ex, ey);
11340 ChangeEvent[ex][ey] = ChangeEvent[x][y];
11342 content_element = change->target_content.e[xx][yy];
11343 target_element = GET_TARGET_ELEMENT(element, content_element, change,
11344 ce_value, ce_score);
11346 CreateElementFromChange(ex, ey, target_element);
11348 something_has_changed = TRUE;
11350 /* for symmetry reasons, freeze newly created border elements */
11351 if (ex != x || ey != y)
11352 Stop[ex][ey] = TRUE; /* no more moving in this frame */
11356 if (something_has_changed)
11358 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11359 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11365 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11366 ce_value, ce_score);
11368 if (element == EL_DIAGONAL_GROWING ||
11369 element == EL_DIAGONAL_SHRINKING)
11371 target_element = Store[x][y];
11373 Store[x][y] = EL_EMPTY;
11376 CreateElementFromChange(x, y, target_element);
11378 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11379 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11382 /* this uses direct change before indirect change */
11383 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11388 #if USE_NEW_DELAYED_ACTION
11390 static void HandleElementChange(int x, int y, int page)
11392 int element = MovingOrBlocked2Element(x, y);
11393 struct ElementInfo *ei = &element_info[element];
11394 struct ElementChangeInfo *change = &ei->change_page[page];
11395 boolean handle_action_before_change = FALSE;
11398 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11399 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11402 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11403 x, y, element, element_info[element].token_name);
11404 printf("HandleElementChange(): This should never happen!\n");
11409 /* this can happen with classic bombs on walkable, changing elements */
11410 if (!CAN_CHANGE_OR_HAS_ACTION(element))
11413 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11414 ChangeDelay[x][y] = 0;
11420 if (ChangeDelay[x][y] == 0) /* initialize element change */
11422 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11424 if (change->can_change)
11427 /* !!! not clear why graphic animation should be reset at all here !!! */
11428 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11429 #if USE_GFX_RESET_WHEN_NOT_MOVING
11430 /* when a custom element is about to change (for example by change delay),
11431 do not reset graphic animation when the custom element is moving */
11432 if (!IS_MOVING(x, y))
11435 ResetGfxAnimation(x, y);
11436 ResetRandomAnimationValue(x, y);
11440 if (change->pre_change_function)
11441 change->pre_change_function(x, y);
11445 ChangeDelay[x][y]--;
11447 if (ChangeDelay[x][y] != 0) /* continue element change */
11449 if (change->can_change)
11451 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11453 if (IS_ANIMATED(graphic))
11454 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11456 if (change->change_function)
11457 change->change_function(x, y);
11460 else /* finish element change */
11462 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11464 page = ChangePage[x][y];
11465 ChangePage[x][y] = -1;
11467 change = &ei->change_page[page];
11470 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11472 ChangeDelay[x][y] = 1; /* try change after next move step */
11473 ChangePage[x][y] = page; /* remember page to use for change */
11479 /* special case: set new level random seed before changing element */
11480 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11481 handle_action_before_change = TRUE;
11483 if (change->has_action && handle_action_before_change)
11484 ExecuteCustomElementAction(x, y, element, page);
11487 if (change->can_change)
11489 if (ChangeElement(x, y, element, page))
11491 if (change->post_change_function)
11492 change->post_change_function(x, y);
11496 if (change->has_action && !handle_action_before_change)
11497 ExecuteCustomElementAction(x, y, element, page);
11503 static void HandleElementChange(int x, int y, int page)
11505 int element = MovingOrBlocked2Element(x, y);
11506 struct ElementInfo *ei = &element_info[element];
11507 struct ElementChangeInfo *change = &ei->change_page[page];
11510 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11513 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11514 x, y, element, element_info[element].token_name);
11515 printf("HandleElementChange(): This should never happen!\n");
11520 /* this can happen with classic bombs on walkable, changing elements */
11521 if (!CAN_CHANGE(element))
11524 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11525 ChangeDelay[x][y] = 0;
11531 if (ChangeDelay[x][y] == 0) /* initialize element change */
11533 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11535 ResetGfxAnimation(x, y);
11536 ResetRandomAnimationValue(x, y);
11538 if (change->pre_change_function)
11539 change->pre_change_function(x, y);
11542 ChangeDelay[x][y]--;
11544 if (ChangeDelay[x][y] != 0) /* continue element change */
11546 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11548 if (IS_ANIMATED(graphic))
11549 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11551 if (change->change_function)
11552 change->change_function(x, y);
11554 else /* finish element change */
11556 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11558 page = ChangePage[x][y];
11559 ChangePage[x][y] = -1;
11561 change = &ei->change_page[page];
11564 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11566 ChangeDelay[x][y] = 1; /* try change after next move step */
11567 ChangePage[x][y] = page; /* remember page to use for change */
11572 if (ChangeElement(x, y, element, page))
11574 if (change->post_change_function)
11575 change->post_change_function(x, y);
11582 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11583 int trigger_element,
11585 int trigger_player,
11589 boolean change_done_any = FALSE;
11590 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11593 if (!(trigger_events[trigger_element][trigger_event]))
11597 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11598 trigger_event, recursion_loop_depth, recursion_loop_detected,
11599 recursion_loop_element, EL_NAME(recursion_loop_element));
11602 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11604 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11606 int element = EL_CUSTOM_START + i;
11607 boolean change_done = FALSE;
11610 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11611 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11614 for (p = 0; p < element_info[element].num_change_pages; p++)
11616 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11618 if (change->can_change_or_has_action &&
11619 change->has_event[trigger_event] &&
11620 change->trigger_side & trigger_side &&
11621 change->trigger_player & trigger_player &&
11622 change->trigger_page & trigger_page_bits &&
11623 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11625 change->actual_trigger_element = trigger_element;
11626 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11627 change->actual_trigger_player_bits = trigger_player;
11628 change->actual_trigger_side = trigger_side;
11629 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11630 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11633 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11634 element, EL_NAME(element), p);
11637 if ((change->can_change && !change_done) || change->has_action)
11641 SCAN_PLAYFIELD(x, y)
11643 if (Feld[x][y] == element)
11645 if (change->can_change && !change_done)
11647 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11648 /* if element already changed in this frame, not only prevent
11649 another element change (checked in ChangeElement()), but
11650 also prevent additional element actions for this element */
11652 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11653 !level.use_action_after_change_bug)
11658 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11659 element, EL_NAME(element), p);
11662 ChangeDelay[x][y] = 1;
11663 ChangeEvent[x][y] = trigger_event;
11665 HandleElementChange(x, y, p);
11667 #if USE_NEW_DELAYED_ACTION
11668 else if (change->has_action)
11670 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11671 /* if element already changed in this frame, not only prevent
11672 another element change (checked in ChangeElement()), but
11673 also prevent additional element actions for this element */
11675 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11676 !level.use_action_after_change_bug)
11682 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11683 element, EL_NAME(element), p);
11686 ExecuteCustomElementAction(x, y, element, p);
11687 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11690 if (change->has_action)
11692 ExecuteCustomElementAction(x, y, element, p);
11693 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11699 if (change->can_change)
11701 change_done = TRUE;
11702 change_done_any = TRUE;
11705 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11706 element, EL_NAME(element), p);
11715 RECURSION_LOOP_DETECTION_END();
11717 return change_done_any;
11720 static boolean CheckElementChangeExt(int x, int y,
11722 int trigger_element,
11724 int trigger_player,
11727 boolean change_done = FALSE;
11730 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11731 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11734 if (Feld[x][y] == EL_BLOCKED)
11736 Blocked2Moving(x, y, &x, &y);
11737 element = Feld[x][y];
11741 /* check if element has already changed */
11742 if (Feld[x][y] != element)
11745 /* check if element has already changed or is about to change after moving */
11746 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11747 Feld[x][y] != element) ||
11749 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11750 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11751 ChangePage[x][y] != -1)))
11756 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11757 trigger_event, recursion_loop_depth, recursion_loop_detected,
11758 recursion_loop_element, EL_NAME(recursion_loop_element));
11761 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11764 printf("::: X: trigger_player_bits == %d\n", trigger_player);
11767 for (p = 0; p < element_info[element].num_change_pages; p++)
11769 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11771 /* check trigger element for all events where the element that is checked
11772 for changing interacts with a directly adjacent element -- this is
11773 different to element changes that affect other elements to change on the
11774 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11775 boolean check_trigger_element =
11776 (trigger_event == CE_TOUCHING_X ||
11777 trigger_event == CE_HITTING_X ||
11778 trigger_event == CE_HIT_BY_X ||
11780 /* this one was forgotten until 3.2.3 */
11781 trigger_event == CE_DIGGING_X);
11784 if (change->can_change_or_has_action &&
11785 change->has_event[trigger_event] &&
11786 change->trigger_side & trigger_side &&
11787 change->trigger_player & trigger_player &&
11788 (!check_trigger_element ||
11789 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11791 change->actual_trigger_element = trigger_element;
11792 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11793 change->actual_trigger_player_bits = trigger_player;
11794 change->actual_trigger_side = trigger_side;
11795 change->actual_trigger_ce_value = CustomValue[x][y];
11796 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11798 /* special case: trigger element not at (x,y) position for some events */
11799 if (check_trigger_element)
11811 { 0, 0 }, { 0, 0 }, { 0, 0 },
11815 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11816 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11818 change->actual_trigger_ce_value = CustomValue[xx][yy];
11819 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11822 if (change->can_change && !change_done)
11824 ChangeDelay[x][y] = 1;
11825 ChangeEvent[x][y] = trigger_event;
11827 HandleElementChange(x, y, p);
11829 change_done = TRUE;
11831 #if USE_NEW_DELAYED_ACTION
11832 else if (change->has_action)
11834 ExecuteCustomElementAction(x, y, element, p);
11835 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11838 if (change->has_action)
11840 ExecuteCustomElementAction(x, y, element, p);
11841 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11847 RECURSION_LOOP_DETECTION_END();
11849 return change_done;
11852 static void PlayPlayerSound(struct PlayerInfo *player)
11854 int jx = player->jx, jy = player->jy;
11855 int sound_element = player->artwork_element;
11856 int last_action = player->last_action_waiting;
11857 int action = player->action_waiting;
11859 if (player->is_waiting)
11861 if (action != last_action)
11862 PlayLevelSoundElementAction(jx, jy, sound_element, action);
11864 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11868 if (action != last_action)
11869 StopSound(element_info[sound_element].sound[last_action]);
11871 if (last_action == ACTION_SLEEPING)
11872 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11876 static void PlayAllPlayersSound()
11880 for (i = 0; i < MAX_PLAYERS; i++)
11881 if (stored_player[i].active)
11882 PlayPlayerSound(&stored_player[i]);
11885 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11887 boolean last_waiting = player->is_waiting;
11888 int move_dir = player->MovDir;
11890 player->dir_waiting = move_dir;
11891 player->last_action_waiting = player->action_waiting;
11895 if (!last_waiting) /* not waiting -> waiting */
11897 player->is_waiting = TRUE;
11899 player->frame_counter_bored =
11901 game.player_boring_delay_fixed +
11902 GetSimpleRandom(game.player_boring_delay_random);
11903 player->frame_counter_sleeping =
11905 game.player_sleeping_delay_fixed +
11906 GetSimpleRandom(game.player_sleeping_delay_random);
11908 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11911 if (game.player_sleeping_delay_fixed +
11912 game.player_sleeping_delay_random > 0 &&
11913 player->anim_delay_counter == 0 &&
11914 player->post_delay_counter == 0 &&
11915 FrameCounter >= player->frame_counter_sleeping)
11916 player->is_sleeping = TRUE;
11917 else if (game.player_boring_delay_fixed +
11918 game.player_boring_delay_random > 0 &&
11919 FrameCounter >= player->frame_counter_bored)
11920 player->is_bored = TRUE;
11922 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11923 player->is_bored ? ACTION_BORING :
11926 if (player->is_sleeping && player->use_murphy)
11928 /* special case for sleeping Murphy when leaning against non-free tile */
11930 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11931 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11932 !IS_MOVING(player->jx - 1, player->jy)))
11933 move_dir = MV_LEFT;
11934 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11935 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11936 !IS_MOVING(player->jx + 1, player->jy)))
11937 move_dir = MV_RIGHT;
11939 player->is_sleeping = FALSE;
11941 player->dir_waiting = move_dir;
11944 if (player->is_sleeping)
11946 if (player->num_special_action_sleeping > 0)
11948 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11950 int last_special_action = player->special_action_sleeping;
11951 int num_special_action = player->num_special_action_sleeping;
11952 int special_action =
11953 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11954 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11955 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11956 last_special_action + 1 : ACTION_SLEEPING);
11957 int special_graphic =
11958 el_act_dir2img(player->artwork_element, special_action, move_dir);
11960 player->anim_delay_counter =
11961 graphic_info[special_graphic].anim_delay_fixed +
11962 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11963 player->post_delay_counter =
11964 graphic_info[special_graphic].post_delay_fixed +
11965 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11967 player->special_action_sleeping = special_action;
11970 if (player->anim_delay_counter > 0)
11972 player->action_waiting = player->special_action_sleeping;
11973 player->anim_delay_counter--;
11975 else if (player->post_delay_counter > 0)
11977 player->post_delay_counter--;
11981 else if (player->is_bored)
11983 if (player->num_special_action_bored > 0)
11985 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11987 int special_action =
11988 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11989 int special_graphic =
11990 el_act_dir2img(player->artwork_element, special_action, move_dir);
11992 player->anim_delay_counter =
11993 graphic_info[special_graphic].anim_delay_fixed +
11994 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11995 player->post_delay_counter =
11996 graphic_info[special_graphic].post_delay_fixed +
11997 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11999 player->special_action_bored = special_action;
12002 if (player->anim_delay_counter > 0)
12004 player->action_waiting = player->special_action_bored;
12005 player->anim_delay_counter--;
12007 else if (player->post_delay_counter > 0)
12009 player->post_delay_counter--;
12014 else if (last_waiting) /* waiting -> not waiting */
12016 player->is_waiting = FALSE;
12017 player->is_bored = FALSE;
12018 player->is_sleeping = FALSE;
12020 player->frame_counter_bored = -1;
12021 player->frame_counter_sleeping = -1;
12023 player->anim_delay_counter = 0;
12024 player->post_delay_counter = 0;
12026 player->dir_waiting = player->MovDir;
12027 player->action_waiting = ACTION_DEFAULT;
12029 player->special_action_bored = ACTION_DEFAULT;
12030 player->special_action_sleeping = ACTION_DEFAULT;
12034 static void CheckSingleStepMode(struct PlayerInfo *player)
12036 if (tape.single_step && tape.recording && !tape.pausing)
12038 /* as it is called "single step mode", just return to pause mode when the
12039 player stopped moving after one tile (or never starts moving at all) */
12040 if (!player->is_moving && !player->is_pushing)
12042 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12043 SnapField(player, 0, 0); /* stop snapping */
12048 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
12050 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
12051 int left = player_action & JOY_LEFT;
12052 int right = player_action & JOY_RIGHT;
12053 int up = player_action & JOY_UP;
12054 int down = player_action & JOY_DOWN;
12055 int button1 = player_action & JOY_BUTTON_1;
12056 int button2 = player_action & JOY_BUTTON_2;
12057 int dx = (left ? -1 : right ? 1 : 0);
12058 int dy = (up ? -1 : down ? 1 : 0);
12060 if (!player->active || tape.pausing)
12066 snapped = SnapField(player, dx, dy);
12070 dropped = DropElement(player);
12072 moved = MovePlayer(player, dx, dy);
12075 CheckSingleStepMode(player);
12077 SetPlayerWaiting(player, FALSE);
12079 return player_action;
12083 /* no actions for this player (no input at player's configured device) */
12085 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
12086 SnapField(player, 0, 0);
12087 CheckGravityMovementWhenNotMoving(player);
12089 if (player->MovPos == 0)
12090 SetPlayerWaiting(player, TRUE);
12092 if (player->MovPos == 0) /* needed for tape.playing */
12093 player->is_moving = FALSE;
12095 player->is_dropping = FALSE;
12096 player->is_dropping_pressed = FALSE;
12097 player->drop_pressed_delay = 0;
12099 CheckSingleStepMode(player);
12105 static void CheckLevelTime()
12109 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
12110 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12112 if (level.native_em_level->lev->home == 0) /* all players at home */
12114 PlayerWins(local_player);
12116 AllPlayersGone = TRUE;
12118 level.native_em_level->lev->home = -1;
12121 if (level.native_em_level->ply[0]->alive == 0 &&
12122 level.native_em_level->ply[1]->alive == 0 &&
12123 level.native_em_level->ply[2]->alive == 0 &&
12124 level.native_em_level->ply[3]->alive == 0) /* all dead */
12125 AllPlayersGone = TRUE;
12127 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12129 if (game_sp.LevelSolved &&
12130 !game_sp.GameOver) /* game won */
12132 PlayerWins(local_player);
12134 game_sp.GameOver = TRUE;
12136 AllPlayersGone = TRUE;
12139 if (game_sp.GameOver) /* game lost */
12140 AllPlayersGone = TRUE;
12143 if (TimeFrames >= FRAMES_PER_SECOND)
12148 for (i = 0; i < MAX_PLAYERS; i++)
12150 struct PlayerInfo *player = &stored_player[i];
12152 if (SHIELD_ON(player))
12154 player->shield_normal_time_left--;
12156 if (player->shield_deadly_time_left > 0)
12157 player->shield_deadly_time_left--;
12161 if (!local_player->LevelSolved && !level.use_step_counter)
12169 if (TimeLeft <= 10 && setup.time_limit)
12170 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12173 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12175 DisplayGameControlValues();
12177 DrawGameValue_Time(TimeLeft);
12180 if (!TimeLeft && setup.time_limit)
12182 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12183 level.native_em_level->lev->killed_out_of_time = TRUE;
12185 for (i = 0; i < MAX_PLAYERS; i++)
12186 KillPlayer(&stored_player[i]);
12190 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12192 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12194 DisplayGameControlValues();
12197 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12198 DrawGameValue_Time(TimePlayed);
12201 level.native_em_level->lev->time =
12202 (level.time == 0 ? TimePlayed : TimeLeft);
12205 if (tape.recording || tape.playing)
12206 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12210 UpdateAndDisplayGameControlValues();
12212 UpdateGameDoorValues();
12213 DrawGameDoorValues();
12217 void AdvanceFrameAndPlayerCounters(int player_nr)
12221 /* advance frame counters (global frame counter and time frame counter) */
12225 /* advance player counters (counters for move delay, move animation etc.) */
12226 for (i = 0; i < MAX_PLAYERS; i++)
12228 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12229 int move_delay_value = stored_player[i].move_delay_value;
12230 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12232 if (!advance_player_counters) /* not all players may be affected */
12235 #if USE_NEW_PLAYER_ANIM
12236 if (move_frames == 0) /* less than one move per game frame */
12238 int stepsize = TILEX / move_delay_value;
12239 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12240 int count = (stored_player[i].is_moving ?
12241 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12243 if (count % delay == 0)
12248 stored_player[i].Frame += move_frames;
12250 if (stored_player[i].MovPos != 0)
12251 stored_player[i].StepFrame += move_frames;
12253 if (stored_player[i].move_delay > 0)
12254 stored_player[i].move_delay--;
12256 /* due to bugs in previous versions, counter must count up, not down */
12257 if (stored_player[i].push_delay != -1)
12258 stored_player[i].push_delay++;
12260 if (stored_player[i].drop_delay > 0)
12261 stored_player[i].drop_delay--;
12263 if (stored_player[i].is_dropping_pressed)
12264 stored_player[i].drop_pressed_delay++;
12268 void StartGameActions(boolean init_network_game, boolean record_tape,
12271 unsigned long new_random_seed = InitRND(random_seed);
12274 TapeStartRecording(new_random_seed);
12276 #if defined(NETWORK_AVALIABLE)
12277 if (init_network_game)
12279 SendToServer_StartPlaying();
12290 static unsigned long game_frame_delay = 0;
12291 unsigned long game_frame_delay_value;
12292 byte *recorded_player_action;
12293 byte summarized_player_action = 0;
12294 byte tape_action[MAX_PLAYERS];
12297 /* detect endless loops, caused by custom element programming */
12298 if (recursion_loop_detected && recursion_loop_depth == 0)
12300 char *message = getStringCat3("Internal Error ! Element ",
12301 EL_NAME(recursion_loop_element),
12302 " caused endless loop ! Quit the game ?");
12304 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12305 EL_NAME(recursion_loop_element));
12307 RequestQuitGameExt(FALSE, level_editor_test_game, message);
12309 recursion_loop_detected = FALSE; /* if game should be continued */
12316 if (game.restart_level)
12317 StartGameActions(options.network, setup.autorecord, level.random_seed);
12319 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
12320 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12322 if (level.native_em_level->lev->home == 0) /* all players at home */
12324 PlayerWins(local_player);
12326 AllPlayersGone = TRUE;
12328 level.native_em_level->lev->home = -1;
12331 if (level.native_em_level->ply[0]->alive == 0 &&
12332 level.native_em_level->ply[1]->alive == 0 &&
12333 level.native_em_level->ply[2]->alive == 0 &&
12334 level.native_em_level->ply[3]->alive == 0) /* all dead */
12335 AllPlayersGone = TRUE;
12337 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12339 if (game_sp.LevelSolved &&
12340 !game_sp.GameOver) /* game won */
12342 PlayerWins(local_player);
12344 game_sp.GameOver = TRUE;
12346 AllPlayersGone = TRUE;
12349 if (game_sp.GameOver) /* game lost */
12350 AllPlayersGone = TRUE;
12353 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12356 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12359 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
12362 game_frame_delay_value =
12363 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12365 if (tape.playing && tape.warp_forward && !tape.pausing)
12366 game_frame_delay_value = 0;
12368 /* ---------- main game synchronization point ---------- */
12370 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12372 if (network_playing && !network_player_action_received)
12374 /* try to get network player actions in time */
12376 #if defined(NETWORK_AVALIABLE)
12377 /* last chance to get network player actions without main loop delay */
12378 HandleNetworking();
12381 /* game was quit by network peer */
12382 if (game_status != GAME_MODE_PLAYING)
12385 if (!network_player_action_received)
12386 return; /* failed to get network player actions in time */
12388 /* do not yet reset "network_player_action_received" (for tape.pausing) */
12394 /* at this point we know that we really continue executing the game */
12396 network_player_action_received = FALSE;
12398 /* when playing tape, read previously recorded player input from tape data */
12399 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12402 /* TapePlayAction() may return NULL when toggling to "pause before death" */
12407 if (tape.set_centered_player)
12409 game.centered_player_nr_next = tape.centered_player_nr_next;
12410 game.set_centered_player = TRUE;
12413 for (i = 0; i < MAX_PLAYERS; i++)
12415 summarized_player_action |= stored_player[i].action;
12417 if (!network_playing)
12418 stored_player[i].effective_action = stored_player[i].action;
12421 #if defined(NETWORK_AVALIABLE)
12422 if (network_playing)
12423 SendToServer_MovePlayer(summarized_player_action);
12426 if (!options.network && !setup.team_mode)
12427 local_player->effective_action = summarized_player_action;
12429 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
12431 for (i = 0; i < MAX_PLAYERS; i++)
12432 stored_player[i].effective_action =
12433 (i == game.centered_player_nr ? summarized_player_action : 0);
12436 if (recorded_player_action != NULL)
12437 for (i = 0; i < MAX_PLAYERS; i++)
12438 stored_player[i].effective_action = recorded_player_action[i];
12440 for (i = 0; i < MAX_PLAYERS; i++)
12442 tape_action[i] = stored_player[i].effective_action;
12444 /* (this can only happen in the R'n'D game engine) */
12445 if (tape.recording && tape_action[i] && !tape.player_participates[i])
12446 tape.player_participates[i] = TRUE; /* player just appeared from CE */
12449 /* only record actions from input devices, but not programmed actions */
12450 if (tape.recording)
12451 TapeRecordAction(tape_action);
12453 #if USE_NEW_PLAYER_ASSIGNMENTS
12455 byte mapped_action[MAX_PLAYERS];
12457 for (i = 0; i < MAX_PLAYERS; i++)
12458 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12460 for (i = 0; i < MAX_PLAYERS; i++)
12461 stored_player[i].effective_action = mapped_action[i];
12465 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12467 GameActions_EM_Main();
12469 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12471 GameActions_SP_Main();
12479 void GameActions_EM_Main()
12481 byte effective_action[MAX_PLAYERS];
12482 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12485 for (i = 0; i < MAX_PLAYERS; i++)
12486 effective_action[i] = stored_player[i].effective_action;
12488 GameActions_EM(effective_action, warp_mode);
12492 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12495 void GameActions_SP_Main()
12497 byte effective_action[MAX_PLAYERS];
12498 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12501 for (i = 0; i < MAX_PLAYERS; i++)
12502 effective_action[i] = stored_player[i].effective_action;
12504 GameActions_SP(effective_action, warp_mode);
12508 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12511 void GameActions_RND()
12513 int magic_wall_x = 0, magic_wall_y = 0;
12514 int i, x, y, element, graphic;
12516 InitPlayfieldScanModeVars();
12518 #if USE_ONE_MORE_CHANGE_PER_FRAME
12519 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12521 SCAN_PLAYFIELD(x, y)
12523 ChangeCount[x][y] = 0;
12524 ChangeEvent[x][y] = -1;
12529 if (game.set_centered_player)
12531 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12533 /* switching to "all players" only possible if all players fit to screen */
12534 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12536 game.centered_player_nr_next = game.centered_player_nr;
12537 game.set_centered_player = FALSE;
12540 /* do not switch focus to non-existing (or non-active) player */
12541 if (game.centered_player_nr_next >= 0 &&
12542 !stored_player[game.centered_player_nr_next].active)
12544 game.centered_player_nr_next = game.centered_player_nr;
12545 game.set_centered_player = FALSE;
12549 if (game.set_centered_player &&
12550 ScreenMovPos == 0) /* screen currently aligned at tile position */
12554 if (game.centered_player_nr_next == -1)
12556 setScreenCenteredToAllPlayers(&sx, &sy);
12560 sx = stored_player[game.centered_player_nr_next].jx;
12561 sy = stored_player[game.centered_player_nr_next].jy;
12564 game.centered_player_nr = game.centered_player_nr_next;
12565 game.set_centered_player = FALSE;
12567 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12568 DrawGameDoorValues();
12571 for (i = 0; i < MAX_PLAYERS; i++)
12573 int actual_player_action = stored_player[i].effective_action;
12576 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12577 - rnd_equinox_tetrachloride 048
12578 - rnd_equinox_tetrachloride_ii 096
12579 - rnd_emanuel_schmieg 002
12580 - doctor_sloan_ww 001, 020
12582 if (stored_player[i].MovPos == 0)
12583 CheckGravityMovement(&stored_player[i]);
12586 /* overwrite programmed action with tape action */
12587 if (stored_player[i].programmed_action)
12588 actual_player_action = stored_player[i].programmed_action;
12590 PlayerActions(&stored_player[i], actual_player_action);
12592 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12595 ScrollScreen(NULL, SCROLL_GO_ON);
12597 /* for backwards compatibility, the following code emulates a fixed bug that
12598 occured when pushing elements (causing elements that just made their last
12599 pushing step to already (if possible) make their first falling step in the
12600 same game frame, which is bad); this code is also needed to use the famous
12601 "spring push bug" which is used in older levels and might be wanted to be
12602 used also in newer levels, but in this case the buggy pushing code is only
12603 affecting the "spring" element and no other elements */
12605 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12607 for (i = 0; i < MAX_PLAYERS; i++)
12609 struct PlayerInfo *player = &stored_player[i];
12610 int x = player->jx;
12611 int y = player->jy;
12613 if (player->active && player->is_pushing && player->is_moving &&
12615 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12616 Feld[x][y] == EL_SPRING))
12618 ContinueMoving(x, y);
12620 /* continue moving after pushing (this is actually a bug) */
12621 if (!IS_MOVING(x, y))
12622 Stop[x][y] = FALSE;
12628 debug_print_timestamp(0, "start main loop profiling");
12631 SCAN_PLAYFIELD(x, y)
12633 ChangeCount[x][y] = 0;
12634 ChangeEvent[x][y] = -1;
12636 /* this must be handled before main playfield loop */
12637 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12640 if (MovDelay[x][y] <= 0)
12644 #if USE_NEW_SNAP_DELAY
12645 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12648 if (MovDelay[x][y] <= 0)
12651 TEST_DrawLevelField(x, y);
12653 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12659 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12661 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12662 printf("GameActions(): This should never happen!\n");
12664 ChangePage[x][y] = -1;
12668 Stop[x][y] = FALSE;
12669 if (WasJustMoving[x][y] > 0)
12670 WasJustMoving[x][y]--;
12671 if (WasJustFalling[x][y] > 0)
12672 WasJustFalling[x][y]--;
12673 if (CheckCollision[x][y] > 0)
12674 CheckCollision[x][y]--;
12675 if (CheckImpact[x][y] > 0)
12676 CheckImpact[x][y]--;
12680 /* reset finished pushing action (not done in ContinueMoving() to allow
12681 continuous pushing animation for elements with zero push delay) */
12682 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12684 ResetGfxAnimation(x, y);
12685 TEST_DrawLevelField(x, y);
12689 if (IS_BLOCKED(x, y))
12693 Blocked2Moving(x, y, &oldx, &oldy);
12694 if (!IS_MOVING(oldx, oldy))
12696 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12697 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12698 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12699 printf("GameActions(): This should never happen!\n");
12706 debug_print_timestamp(0, "- time for pre-main loop:");
12709 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
12710 SCAN_PLAYFIELD(x, y)
12712 element = Feld[x][y];
12713 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12718 int element2 = element;
12719 int graphic2 = graphic;
12721 int element2 = Feld[x][y];
12722 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12724 int last_gfx_frame = GfxFrame[x][y];
12726 if (graphic_info[graphic2].anim_global_sync)
12727 GfxFrame[x][y] = FrameCounter;
12728 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12729 GfxFrame[x][y] = CustomValue[x][y];
12730 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12731 GfxFrame[x][y] = element_info[element2].collect_score;
12732 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12733 GfxFrame[x][y] = ChangeDelay[x][y];
12735 if (redraw && GfxFrame[x][y] != last_gfx_frame)
12736 DrawLevelGraphicAnimation(x, y, graphic2);
12739 ResetGfxFrame(x, y, TRUE);
12743 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12744 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12745 ResetRandomAnimationValue(x, y);
12749 SetRandomAnimationValue(x, y);
12753 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12756 #endif // -------------------- !!! TEST ONLY !!! --------------------
12759 debug_print_timestamp(0, "- time for TEST loop: -->");
12762 SCAN_PLAYFIELD(x, y)
12764 element = Feld[x][y];
12765 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12767 ResetGfxFrame(x, y, TRUE);
12769 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12770 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12771 ResetRandomAnimationValue(x, y);
12773 SetRandomAnimationValue(x, y);
12775 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12777 if (IS_INACTIVE(element))
12779 if (IS_ANIMATED(graphic))
12780 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12785 /* this may take place after moving, so 'element' may have changed */
12786 if (IS_CHANGING(x, y) &&
12787 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12789 int page = element_info[element].event_page_nr[CE_DELAY];
12792 HandleElementChange(x, y, page);
12794 if (CAN_CHANGE(element))
12795 HandleElementChange(x, y, page);
12797 if (HAS_ACTION(element))
12798 ExecuteCustomElementAction(x, y, element, page);
12801 element = Feld[x][y];
12802 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12805 #if 0 // ---------------------------------------------------------------------
12807 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12811 element = Feld[x][y];
12812 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12814 if (IS_ANIMATED(graphic) &&
12815 !IS_MOVING(x, y) &&
12817 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12819 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12820 TEST_DrawTwinkleOnField(x, y);
12822 else if (IS_MOVING(x, y))
12823 ContinueMoving(x, y);
12830 case EL_EM_EXIT_OPEN:
12831 case EL_SP_EXIT_OPEN:
12832 case EL_STEEL_EXIT_OPEN:
12833 case EL_EM_STEEL_EXIT_OPEN:
12834 case EL_SP_TERMINAL:
12835 case EL_SP_TERMINAL_ACTIVE:
12836 case EL_EXTRA_TIME:
12837 case EL_SHIELD_NORMAL:
12838 case EL_SHIELD_DEADLY:
12839 if (IS_ANIMATED(graphic))
12840 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12843 case EL_DYNAMITE_ACTIVE:
12844 case EL_EM_DYNAMITE_ACTIVE:
12845 case EL_DYNABOMB_PLAYER_1_ACTIVE:
12846 case EL_DYNABOMB_PLAYER_2_ACTIVE:
12847 case EL_DYNABOMB_PLAYER_3_ACTIVE:
12848 case EL_DYNABOMB_PLAYER_4_ACTIVE:
12849 case EL_SP_DISK_RED_ACTIVE:
12850 CheckDynamite(x, y);
12853 case EL_AMOEBA_GROWING:
12854 AmoebeWaechst(x, y);
12857 case EL_AMOEBA_SHRINKING:
12858 AmoebaDisappearing(x, y);
12861 #if !USE_NEW_AMOEBA_CODE
12862 case EL_AMOEBA_WET:
12863 case EL_AMOEBA_DRY:
12864 case EL_AMOEBA_FULL:
12866 case EL_EMC_DRIPPER:
12867 AmoebeAbleger(x, y);
12871 case EL_GAME_OF_LIFE:
12876 case EL_EXIT_CLOSED:
12880 case EL_EM_EXIT_CLOSED:
12884 case EL_STEEL_EXIT_CLOSED:
12885 CheckExitSteel(x, y);
12888 case EL_EM_STEEL_EXIT_CLOSED:
12889 CheckExitSteelEM(x, y);
12892 case EL_SP_EXIT_CLOSED:
12896 case EL_EXPANDABLE_WALL_GROWING:
12897 case EL_EXPANDABLE_STEELWALL_GROWING:
12898 MauerWaechst(x, y);
12901 case EL_EXPANDABLE_WALL:
12902 case EL_EXPANDABLE_WALL_HORIZONTAL:
12903 case EL_EXPANDABLE_WALL_VERTICAL:
12904 case EL_EXPANDABLE_WALL_ANY:
12905 case EL_BD_EXPANDABLE_WALL:
12906 MauerAbleger(x, y);
12909 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12910 case EL_EXPANDABLE_STEELWALL_VERTICAL:
12911 case EL_EXPANDABLE_STEELWALL_ANY:
12912 MauerAblegerStahl(x, y);
12916 CheckForDragon(x, y);
12922 case EL_ELEMENT_SNAPPING:
12923 case EL_DIAGONAL_SHRINKING:
12924 case EL_DIAGONAL_GROWING:
12927 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12929 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12934 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12935 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12940 #else // ---------------------------------------------------------------------
12942 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12946 element = Feld[x][y];
12947 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12949 if (IS_ANIMATED(graphic) &&
12950 !IS_MOVING(x, y) &&
12952 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12954 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12955 TEST_DrawTwinkleOnField(x, y);
12957 else if ((element == EL_ACID ||
12958 element == EL_EXIT_OPEN ||
12959 element == EL_EM_EXIT_OPEN ||
12960 element == EL_SP_EXIT_OPEN ||
12961 element == EL_STEEL_EXIT_OPEN ||
12962 element == EL_EM_STEEL_EXIT_OPEN ||
12963 element == EL_SP_TERMINAL ||
12964 element == EL_SP_TERMINAL_ACTIVE ||
12965 element == EL_EXTRA_TIME ||
12966 element == EL_SHIELD_NORMAL ||
12967 element == EL_SHIELD_DEADLY) &&
12968 IS_ANIMATED(graphic))
12969 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12970 else if (IS_MOVING(x, y))
12971 ContinueMoving(x, y);
12972 else if (IS_ACTIVE_BOMB(element))
12973 CheckDynamite(x, y);
12974 else if (element == EL_AMOEBA_GROWING)
12975 AmoebeWaechst(x, y);
12976 else if (element == EL_AMOEBA_SHRINKING)
12977 AmoebaDisappearing(x, y);
12979 #if !USE_NEW_AMOEBA_CODE
12980 else if (IS_AMOEBALIVE(element))
12981 AmoebeAbleger(x, y);
12984 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12986 else if (element == EL_EXIT_CLOSED)
12988 else if (element == EL_EM_EXIT_CLOSED)
12990 else if (element == EL_STEEL_EXIT_CLOSED)
12991 CheckExitSteel(x, y);
12992 else if (element == EL_EM_STEEL_EXIT_CLOSED)
12993 CheckExitSteelEM(x, y);
12994 else if (element == EL_SP_EXIT_CLOSED)
12996 else if (element == EL_EXPANDABLE_WALL_GROWING ||
12997 element == EL_EXPANDABLE_STEELWALL_GROWING)
12998 MauerWaechst(x, y);
12999 else if (element == EL_EXPANDABLE_WALL ||
13000 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
13001 element == EL_EXPANDABLE_WALL_VERTICAL ||
13002 element == EL_EXPANDABLE_WALL_ANY ||
13003 element == EL_BD_EXPANDABLE_WALL)
13004 MauerAbleger(x, y);
13005 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
13006 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
13007 element == EL_EXPANDABLE_STEELWALL_ANY)
13008 MauerAblegerStahl(x, y);
13009 else if (element == EL_FLAMES)
13010 CheckForDragon(x, y);
13011 else if (element == EL_EXPLOSION)
13012 ; /* drawing of correct explosion animation is handled separately */
13013 else if (element == EL_ELEMENT_SNAPPING ||
13014 element == EL_DIAGONAL_SHRINKING ||
13015 element == EL_DIAGONAL_GROWING)
13017 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
13019 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13021 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
13022 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13024 #endif // ---------------------------------------------------------------------
13026 if (IS_BELT_ACTIVE(element))
13027 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
13029 if (game.magic_wall_active)
13031 int jx = local_player->jx, jy = local_player->jy;
13033 /* play the element sound at the position nearest to the player */
13034 if ((element == EL_MAGIC_WALL_FULL ||
13035 element == EL_MAGIC_WALL_ACTIVE ||
13036 element == EL_MAGIC_WALL_EMPTYING ||
13037 element == EL_BD_MAGIC_WALL_FULL ||
13038 element == EL_BD_MAGIC_WALL_ACTIVE ||
13039 element == EL_BD_MAGIC_WALL_EMPTYING ||
13040 element == EL_DC_MAGIC_WALL_FULL ||
13041 element == EL_DC_MAGIC_WALL_ACTIVE ||
13042 element == EL_DC_MAGIC_WALL_EMPTYING) &&
13043 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
13052 debug_print_timestamp(0, "- time for MAIN loop: -->");
13055 #if USE_NEW_AMOEBA_CODE
13056 /* new experimental amoeba growth stuff */
13057 if (!(FrameCounter % 8))
13059 static unsigned long random = 1684108901;
13061 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
13063 x = RND(lev_fieldx);
13064 y = RND(lev_fieldy);
13065 element = Feld[x][y];
13067 if (!IS_PLAYER(x,y) &&
13068 (element == EL_EMPTY ||
13069 CAN_GROW_INTO(element) ||
13070 element == EL_QUICKSAND_EMPTY ||
13071 element == EL_QUICKSAND_FAST_EMPTY ||
13072 element == EL_ACID_SPLASH_LEFT ||
13073 element == EL_ACID_SPLASH_RIGHT))
13075 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
13076 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
13077 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
13078 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
13079 Feld[x][y] = EL_AMOEBA_DROP;
13082 random = random * 129 + 1;
13088 if (game.explosions_delayed)
13091 game.explosions_delayed = FALSE;
13093 SCAN_PLAYFIELD(x, y)
13095 element = Feld[x][y];
13097 if (ExplodeField[x][y])
13098 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
13099 else if (element == EL_EXPLOSION)
13100 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
13102 ExplodeField[x][y] = EX_TYPE_NONE;
13105 game.explosions_delayed = TRUE;
13108 if (game.magic_wall_active)
13110 if (!(game.magic_wall_time_left % 4))
13112 int element = Feld[magic_wall_x][magic_wall_y];
13114 if (element == EL_BD_MAGIC_WALL_FULL ||
13115 element == EL_BD_MAGIC_WALL_ACTIVE ||
13116 element == EL_BD_MAGIC_WALL_EMPTYING)
13117 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
13118 else if (element == EL_DC_MAGIC_WALL_FULL ||
13119 element == EL_DC_MAGIC_WALL_ACTIVE ||
13120 element == EL_DC_MAGIC_WALL_EMPTYING)
13121 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
13123 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
13126 if (game.magic_wall_time_left > 0)
13128 game.magic_wall_time_left--;
13130 if (!game.magic_wall_time_left)
13132 SCAN_PLAYFIELD(x, y)
13134 element = Feld[x][y];
13136 if (element == EL_MAGIC_WALL_ACTIVE ||
13137 element == EL_MAGIC_WALL_FULL)
13139 Feld[x][y] = EL_MAGIC_WALL_DEAD;
13140 TEST_DrawLevelField(x, y);
13142 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
13143 element == EL_BD_MAGIC_WALL_FULL)
13145 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
13146 TEST_DrawLevelField(x, y);
13148 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
13149 element == EL_DC_MAGIC_WALL_FULL)
13151 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
13152 TEST_DrawLevelField(x, y);
13156 game.magic_wall_active = FALSE;
13161 if (game.light_time_left > 0)
13163 game.light_time_left--;
13165 if (game.light_time_left == 0)
13166 RedrawAllLightSwitchesAndInvisibleElements();
13169 if (game.timegate_time_left > 0)
13171 game.timegate_time_left--;
13173 if (game.timegate_time_left == 0)
13174 CloseAllOpenTimegates();
13177 if (game.lenses_time_left > 0)
13179 game.lenses_time_left--;
13181 if (game.lenses_time_left == 0)
13182 RedrawAllInvisibleElementsForLenses();
13185 if (game.magnify_time_left > 0)
13187 game.magnify_time_left--;
13189 if (game.magnify_time_left == 0)
13190 RedrawAllInvisibleElementsForMagnifier();
13193 for (i = 0; i < MAX_PLAYERS; i++)
13195 struct PlayerInfo *player = &stored_player[i];
13197 if (SHIELD_ON(player))
13199 if (player->shield_deadly_time_left)
13200 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13201 else if (player->shield_normal_time_left)
13202 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13206 #if USE_DELAYED_GFX_REDRAW
13207 SCAN_PLAYFIELD(x, y)
13210 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13212 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13213 GfxRedraw[x][y] != GFX_REDRAW_NONE)
13216 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13217 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13219 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13220 DrawLevelField(x, y);
13222 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13223 DrawLevelFieldCrumbled(x, y);
13225 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13226 DrawLevelFieldCrumbledNeighbours(x, y);
13228 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13229 DrawTwinkleOnField(x, y);
13232 GfxRedraw[x][y] = GFX_REDRAW_NONE;
13239 PlayAllPlayersSound();
13241 if (options.debug) /* calculate frames per second */
13243 static unsigned long fps_counter = 0;
13244 static int fps_frames = 0;
13245 unsigned long fps_delay_ms = Counter() - fps_counter;
13249 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
13251 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13254 fps_counter = Counter();
13257 redraw_mask |= REDRAW_FPS;
13260 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
13262 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13264 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13266 local_player->show_envelope = 0;
13270 debug_print_timestamp(0, "stop main loop profiling ");
13271 printf("----------------------------------------------------------\n");
13274 /* use random number generator in every frame to make it less predictable */
13275 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13279 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13281 int min_x = x, min_y = y, max_x = x, max_y = y;
13284 for (i = 0; i < MAX_PLAYERS; i++)
13286 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13288 if (!stored_player[i].active || &stored_player[i] == player)
13291 min_x = MIN(min_x, jx);
13292 min_y = MIN(min_y, jy);
13293 max_x = MAX(max_x, jx);
13294 max_y = MAX(max_y, jy);
13297 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13300 static boolean AllPlayersInVisibleScreen()
13304 for (i = 0; i < MAX_PLAYERS; i++)
13306 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13308 if (!stored_player[i].active)
13311 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13318 void ScrollLevel(int dx, int dy)
13321 /* (directly solved in BlitBitmap() now) */
13322 static Bitmap *bitmap_db_field2 = NULL;
13323 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13330 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13331 /* only horizontal XOR vertical scroll direction allowed */
13332 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13337 /* (directly solved in BlitBitmap() now) */
13338 if (bitmap_db_field2 == NULL)
13339 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13341 /* needed when blitting directly to same bitmap -- should not be needed with
13342 recent SDL libraries, but apparently does not work in 1.2.11 directly */
13343 BlitBitmap(drawto_field, bitmap_db_field2,
13344 FX + TILEX * (dx == -1) - softscroll_offset,
13345 FY + TILEY * (dy == -1) - softscroll_offset,
13346 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13347 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13348 FX + TILEX * (dx == 1) - softscroll_offset,
13349 FY + TILEY * (dy == 1) - softscroll_offset);
13350 BlitBitmap(bitmap_db_field2, drawto_field,
13351 FX + TILEX * (dx == 1) - softscroll_offset,
13352 FY + TILEY * (dy == 1) - softscroll_offset,
13353 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13354 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13355 FX + TILEX * (dx == 1) - softscroll_offset,
13356 FY + TILEY * (dy == 1) - softscroll_offset);
13361 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13362 int xsize = (BX2 - BX1 + 1);
13363 int ysize = (BY2 - BY1 + 1);
13364 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13365 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13366 int step = (start < end ? +1 : -1);
13368 for (i = start; i != end; i += step)
13370 BlitBitmap(drawto_field, drawto_field,
13371 FX + TILEX * (dx != 0 ? i + step : 0),
13372 FY + TILEY * (dy != 0 ? i + step : 0),
13373 TILEX * (dx != 0 ? 1 : xsize),
13374 TILEY * (dy != 0 ? 1 : ysize),
13375 FX + TILEX * (dx != 0 ? i : 0),
13376 FY + TILEY * (dy != 0 ? i : 0));
13383 int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX_VAR : 0);
13385 int softscroll_offset = (setup.soft_scrolling ? TILEX_VAR : 0);
13389 int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX : 0);
13391 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13396 BlitBitmap(drawto_field, drawto_field,
13397 FX + TILEX_VAR * (dx == -1) - softscroll_offset,
13398 FY + TILEY_VAR * (dy == -1) - softscroll_offset,
13399 SXSIZE - TILEX_VAR * (dx != 0) + 2 * softscroll_offset,
13400 SYSIZE - TILEY_VAR * (dy != 0) + 2 * softscroll_offset,
13401 FX + TILEX_VAR * (dx == 1) - softscroll_offset,
13402 FY + TILEY_VAR * (dy == 1) - softscroll_offset);
13404 BlitBitmap(drawto_field, drawto_field,
13405 FX + TILEX * (dx == -1) - softscroll_offset,
13406 FY + TILEY * (dy == -1) - softscroll_offset,
13407 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13408 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13409 FX + TILEX * (dx == 1) - softscroll_offset,
13410 FY + TILEY * (dy == 1) - softscroll_offset);
13418 x = (dx == 1 ? BX1 : BX2);
13419 for (y = BY1; y <= BY2; y++)
13420 DrawScreenField(x, y);
13425 y = (dy == 1 ? BY1 : BY2);
13426 for (x = BX1; x <= BX2; x++)
13427 DrawScreenField(x, y);
13430 redraw_mask |= REDRAW_FIELD;
13433 static boolean canFallDown(struct PlayerInfo *player)
13435 int jx = player->jx, jy = player->jy;
13437 return (IN_LEV_FIELD(jx, jy + 1) &&
13438 (IS_FREE(jx, jy + 1) ||
13439 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13440 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13441 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13444 static boolean canPassField(int x, int y, int move_dir)
13446 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13447 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13448 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13449 int nextx = x + dx;
13450 int nexty = y + dy;
13451 int element = Feld[x][y];
13453 return (IS_PASSABLE_FROM(element, opposite_dir) &&
13454 !CAN_MOVE(element) &&
13455 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13456 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13457 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13460 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13462 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13463 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13464 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13468 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13469 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13470 (IS_DIGGABLE(Feld[newx][newy]) ||
13471 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13472 canPassField(newx, newy, move_dir)));
13475 static void CheckGravityMovement(struct PlayerInfo *player)
13477 #if USE_PLAYER_GRAVITY
13478 if (player->gravity && !player->programmed_action)
13480 if (game.gravity && !player->programmed_action)
13483 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13484 int move_dir_vertical = player->effective_action & MV_VERTICAL;
13485 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13486 int jx = player->jx, jy = player->jy;
13487 boolean player_is_moving_to_valid_field =
13488 (!player_is_snapping &&
13489 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13490 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13491 boolean player_can_fall_down = canFallDown(player);
13493 if (player_can_fall_down &&
13494 !player_is_moving_to_valid_field)
13495 player->programmed_action = MV_DOWN;
13499 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13501 return CheckGravityMovement(player);
13503 #if USE_PLAYER_GRAVITY
13504 if (player->gravity && !player->programmed_action)
13506 if (game.gravity && !player->programmed_action)
13509 int jx = player->jx, jy = player->jy;
13510 boolean field_under_player_is_free =
13511 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13512 boolean player_is_standing_on_valid_field =
13513 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13514 (IS_WALKABLE(Feld[jx][jy]) &&
13515 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13517 if (field_under_player_is_free && !player_is_standing_on_valid_field)
13518 player->programmed_action = MV_DOWN;
13523 MovePlayerOneStep()
13524 -----------------------------------------------------------------------------
13525 dx, dy: direction (non-diagonal) to try to move the player to
13526 real_dx, real_dy: direction as read from input device (can be diagonal)
13529 boolean MovePlayerOneStep(struct PlayerInfo *player,
13530 int dx, int dy, int real_dx, int real_dy)
13532 int jx = player->jx, jy = player->jy;
13533 int new_jx = jx + dx, new_jy = jy + dy;
13534 #if !USE_FIXED_DONT_RUN_INTO
13538 boolean player_can_move = !player->cannot_move;
13540 if (!player->active || (!dx && !dy))
13541 return MP_NO_ACTION;
13543 player->MovDir = (dx < 0 ? MV_LEFT :
13544 dx > 0 ? MV_RIGHT :
13546 dy > 0 ? MV_DOWN : MV_NONE);
13548 if (!IN_LEV_FIELD(new_jx, new_jy))
13549 return MP_NO_ACTION;
13551 if (!player_can_move)
13553 if (player->MovPos == 0)
13555 player->is_moving = FALSE;
13556 player->is_digging = FALSE;
13557 player->is_collecting = FALSE;
13558 player->is_snapping = FALSE;
13559 player->is_pushing = FALSE;
13564 if (!options.network && game.centered_player_nr == -1 &&
13565 !AllPlayersInSight(player, new_jx, new_jy))
13566 return MP_NO_ACTION;
13568 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13569 return MP_NO_ACTION;
13572 #if !USE_FIXED_DONT_RUN_INTO
13573 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13575 /* (moved to DigField()) */
13576 if (player_can_move && DONT_RUN_INTO(element))
13578 if (element == EL_ACID && dx == 0 && dy == 1)
13580 SplashAcid(new_jx, new_jy);
13581 Feld[jx][jy] = EL_PLAYER_1;
13582 InitMovingField(jx, jy, MV_DOWN);
13583 Store[jx][jy] = EL_ACID;
13584 ContinueMoving(jx, jy);
13585 BuryPlayer(player);
13588 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13594 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13595 if (can_move != MP_MOVING)
13598 /* check if DigField() has caused relocation of the player */
13599 if (player->jx != jx || player->jy != jy)
13600 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13602 StorePlayer[jx][jy] = 0;
13603 player->last_jx = jx;
13604 player->last_jy = jy;
13605 player->jx = new_jx;
13606 player->jy = new_jy;
13607 StorePlayer[new_jx][new_jy] = player->element_nr;
13609 if (player->move_delay_value_next != -1)
13611 player->move_delay_value = player->move_delay_value_next;
13612 player->move_delay_value_next = -1;
13616 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13618 player->step_counter++;
13620 PlayerVisit[jx][jy] = FrameCounter;
13622 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13623 player->is_moving = TRUE;
13627 /* should better be called in MovePlayer(), but this breaks some tapes */
13628 ScrollPlayer(player, SCROLL_INIT);
13634 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13636 int jx = player->jx, jy = player->jy;
13637 int old_jx = jx, old_jy = jy;
13638 int moved = MP_NO_ACTION;
13640 if (!player->active)
13645 if (player->MovPos == 0)
13647 player->is_moving = FALSE;
13648 player->is_digging = FALSE;
13649 player->is_collecting = FALSE;
13650 player->is_snapping = FALSE;
13651 player->is_pushing = FALSE;
13657 if (player->move_delay > 0)
13660 player->move_delay = -1; /* set to "uninitialized" value */
13662 /* store if player is automatically moved to next field */
13663 player->is_auto_moving = (player->programmed_action != MV_NONE);
13665 /* remove the last programmed player action */
13666 player->programmed_action = 0;
13668 if (player->MovPos)
13670 /* should only happen if pre-1.2 tape recordings are played */
13671 /* this is only for backward compatibility */
13673 int original_move_delay_value = player->move_delay_value;
13676 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
13680 /* scroll remaining steps with finest movement resolution */
13681 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13683 while (player->MovPos)
13685 ScrollPlayer(player, SCROLL_GO_ON);
13686 ScrollScreen(NULL, SCROLL_GO_ON);
13688 AdvanceFrameAndPlayerCounters(player->index_nr);
13694 player->move_delay_value = original_move_delay_value;
13697 player->is_active = FALSE;
13699 if (player->last_move_dir & MV_HORIZONTAL)
13701 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13702 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13706 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13707 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13710 #if USE_FIXED_BORDER_RUNNING_GFX
13711 if (!moved && !player->is_active)
13713 player->is_moving = FALSE;
13714 player->is_digging = FALSE;
13715 player->is_collecting = FALSE;
13716 player->is_snapping = FALSE;
13717 player->is_pushing = FALSE;
13725 if (moved & MP_MOVING && !ScreenMovPos &&
13726 (player->index_nr == game.centered_player_nr ||
13727 game.centered_player_nr == -1))
13729 if (moved & MP_MOVING && !ScreenMovPos &&
13730 (player == local_player || !options.network))
13733 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13734 int offset = game.scroll_delay_value;
13736 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13738 /* actual player has left the screen -- scroll in that direction */
13739 if (jx != old_jx) /* player has moved horizontally */
13740 scroll_x += (jx - old_jx);
13741 else /* player has moved vertically */
13742 scroll_y += (jy - old_jy);
13746 if (jx != old_jx) /* player has moved horizontally */
13748 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
13749 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13750 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13752 /* don't scroll over playfield boundaries */
13753 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13754 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13756 /* don't scroll more than one field at a time */
13757 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13759 /* don't scroll against the player's moving direction */
13760 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
13761 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13762 scroll_x = old_scroll_x;
13764 else /* player has moved vertically */
13766 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
13767 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13768 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13770 /* don't scroll over playfield boundaries */
13771 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13772 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13774 /* don't scroll more than one field at a time */
13775 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13777 /* don't scroll against the player's moving direction */
13778 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
13779 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13780 scroll_y = old_scroll_y;
13784 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13787 if (!options.network && game.centered_player_nr == -1 &&
13788 !AllPlayersInVisibleScreen())
13790 scroll_x = old_scroll_x;
13791 scroll_y = old_scroll_y;
13795 if (!options.network && !AllPlayersInVisibleScreen())
13797 scroll_x = old_scroll_x;
13798 scroll_y = old_scroll_y;
13803 ScrollScreen(player, SCROLL_INIT);
13804 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13809 player->StepFrame = 0;
13811 if (moved & MP_MOVING)
13813 if (old_jx != jx && old_jy == jy)
13814 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13815 else if (old_jx == jx && old_jy != jy)
13816 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13818 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
13820 player->last_move_dir = player->MovDir;
13821 player->is_moving = TRUE;
13822 player->is_snapping = FALSE;
13823 player->is_switching = FALSE;
13824 player->is_dropping = FALSE;
13825 player->is_dropping_pressed = FALSE;
13826 player->drop_pressed_delay = 0;
13829 /* should better be called here than above, but this breaks some tapes */
13830 ScrollPlayer(player, SCROLL_INIT);
13835 CheckGravityMovementWhenNotMoving(player);
13837 player->is_moving = FALSE;
13839 /* at this point, the player is allowed to move, but cannot move right now
13840 (e.g. because of something blocking the way) -- ensure that the player
13841 is also allowed to move in the next frame (in old versions before 3.1.1,
13842 the player was forced to wait again for eight frames before next try) */
13844 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13845 player->move_delay = 0; /* allow direct movement in the next frame */
13848 if (player->move_delay == -1) /* not yet initialized by DigField() */
13849 player->move_delay = player->move_delay_value;
13851 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13853 TestIfPlayerTouchesBadThing(jx, jy);
13854 TestIfPlayerTouchesCustomElement(jx, jy);
13857 if (!player->active)
13858 RemovePlayer(player);
13863 void ScrollPlayer(struct PlayerInfo *player, int mode)
13865 int jx = player->jx, jy = player->jy;
13866 int last_jx = player->last_jx, last_jy = player->last_jy;
13867 int move_stepsize = TILEX / player->move_delay_value;
13869 #if USE_NEW_PLAYER_SPEED
13870 if (!player->active)
13873 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
13876 if (!player->active || player->MovPos == 0)
13880 if (mode == SCROLL_INIT)
13882 player->actual_frame_counter = FrameCounter;
13883 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13885 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13886 Feld[last_jx][last_jy] == EL_EMPTY)
13888 int last_field_block_delay = 0; /* start with no blocking at all */
13889 int block_delay_adjustment = player->block_delay_adjustment;
13891 /* if player blocks last field, add delay for exactly one move */
13892 if (player->block_last_field)
13894 last_field_block_delay += player->move_delay_value;
13896 /* when blocking enabled, prevent moving up despite gravity */
13897 #if USE_PLAYER_GRAVITY
13898 if (player->gravity && player->MovDir == MV_UP)
13899 block_delay_adjustment = -1;
13901 if (game.gravity && player->MovDir == MV_UP)
13902 block_delay_adjustment = -1;
13906 /* add block delay adjustment (also possible when not blocking) */
13907 last_field_block_delay += block_delay_adjustment;
13909 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13910 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13913 #if USE_NEW_PLAYER_SPEED
13914 if (player->MovPos != 0) /* player has not yet reached destination */
13920 else if (!FrameReached(&player->actual_frame_counter, 1))
13923 #if USE_NEW_PLAYER_SPEED
13924 if (player->MovPos != 0)
13926 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13927 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13929 /* before DrawPlayer() to draw correct player graphic for this case */
13930 if (player->MovPos == 0)
13931 CheckGravityMovement(player);
13934 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13935 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13937 /* before DrawPlayer() to draw correct player graphic for this case */
13938 if (player->MovPos == 0)
13939 CheckGravityMovement(player);
13942 if (player->MovPos == 0) /* player reached destination field */
13944 if (player->move_delay_reset_counter > 0)
13946 player->move_delay_reset_counter--;
13948 if (player->move_delay_reset_counter == 0)
13950 /* continue with normal speed after quickly moving through gate */
13951 HALVE_PLAYER_SPEED(player);
13953 /* be able to make the next move without delay */
13954 player->move_delay = 0;
13958 player->last_jx = jx;
13959 player->last_jy = jy;
13961 if (Feld[jx][jy] == EL_EXIT_OPEN ||
13962 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13964 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
13966 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13967 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13969 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13971 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13972 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
13974 DrawPlayer(player); /* needed here only to cleanup last field */
13975 RemovePlayer(player);
13977 if (local_player->friends_still_needed == 0 ||
13978 IS_SP_ELEMENT(Feld[jx][jy]))
13979 PlayerWins(player);
13982 /* this breaks one level: "machine", level 000 */
13984 int move_direction = player->MovDir;
13985 int enter_side = MV_DIR_OPPOSITE(move_direction);
13986 int leave_side = move_direction;
13987 int old_jx = last_jx;
13988 int old_jy = last_jy;
13989 int old_element = Feld[old_jx][old_jy];
13990 int new_element = Feld[jx][jy];
13992 if (IS_CUSTOM_ELEMENT(old_element))
13993 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13995 player->index_bit, leave_side);
13997 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13998 CE_PLAYER_LEAVES_X,
13999 player->index_bit, leave_side);
14001 if (IS_CUSTOM_ELEMENT(new_element))
14002 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
14003 player->index_bit, enter_side);
14005 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
14006 CE_PLAYER_ENTERS_X,
14007 player->index_bit, enter_side);
14009 #if USE_FIX_CE_ACTION_WITH_PLAYER
14010 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
14011 CE_MOVE_OF_X, move_direction);
14013 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
14014 CE_MOVE_OF_X, move_direction);
14018 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14020 TestIfPlayerTouchesBadThing(jx, jy);
14021 TestIfPlayerTouchesCustomElement(jx, jy);
14023 /* needed because pushed element has not yet reached its destination,
14024 so it would trigger a change event at its previous field location */
14025 if (!player->is_pushing)
14026 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
14028 if (!player->active)
14029 RemovePlayer(player);
14032 if (!local_player->LevelSolved && level.use_step_counter)
14042 if (TimeLeft <= 10 && setup.time_limit)
14043 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14046 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14048 DisplayGameControlValues();
14050 DrawGameValue_Time(TimeLeft);
14053 if (!TimeLeft && setup.time_limit)
14054 for (i = 0; i < MAX_PLAYERS; i++)
14055 KillPlayer(&stored_player[i]);
14058 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
14060 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
14062 DisplayGameControlValues();
14065 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
14066 DrawGameValue_Time(TimePlayed);
14070 if (tape.single_step && tape.recording && !tape.pausing &&
14071 !player->programmed_action)
14072 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
14076 void ScrollScreen(struct PlayerInfo *player, int mode)
14078 static unsigned long screen_frame_counter = 0;
14080 if (mode == SCROLL_INIT)
14082 /* set scrolling step size according to actual player's moving speed */
14083 ScrollStepSize = TILEX / player->move_delay_value;
14085 screen_frame_counter = FrameCounter;
14086 ScreenMovDir = player->MovDir;
14087 ScreenMovPos = player->MovPos;
14088 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14091 else if (!FrameReached(&screen_frame_counter, 1))
14096 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
14097 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14098 redraw_mask |= REDRAW_FIELD;
14101 ScreenMovDir = MV_NONE;
14104 void TestIfPlayerTouchesCustomElement(int x, int y)
14106 static int xy[4][2] =
14113 static int trigger_sides[4][2] =
14115 /* center side border side */
14116 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14117 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14118 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14119 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14121 static int touch_dir[4] =
14123 MV_LEFT | MV_RIGHT,
14128 int center_element = Feld[x][y]; /* should always be non-moving! */
14131 for (i = 0; i < NUM_DIRECTIONS; i++)
14133 int xx = x + xy[i][0];
14134 int yy = y + xy[i][1];
14135 int center_side = trigger_sides[i][0];
14136 int border_side = trigger_sides[i][1];
14137 int border_element;
14139 if (!IN_LEV_FIELD(xx, yy))
14142 if (IS_PLAYER(x, y)) /* player found at center element */
14144 struct PlayerInfo *player = PLAYERINFO(x, y);
14146 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14147 border_element = Feld[xx][yy]; /* may be moving! */
14148 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14149 border_element = Feld[xx][yy];
14150 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14151 border_element = MovingOrBlocked2Element(xx, yy);
14153 continue; /* center and border element do not touch */
14155 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
14156 player->index_bit, border_side);
14157 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
14158 CE_PLAYER_TOUCHES_X,
14159 player->index_bit, border_side);
14161 #if USE_FIX_CE_ACTION_WITH_PLAYER
14163 /* use player element that is initially defined in the level playfield,
14164 not the player element that corresponds to the runtime player number
14165 (example: a level that contains EL_PLAYER_3 as the only player would
14166 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14167 int player_element = PLAYERINFO(x, y)->initial_element;
14169 CheckElementChangeBySide(xx, yy, border_element, player_element,
14170 CE_TOUCHING_X, border_side);
14174 else if (IS_PLAYER(xx, yy)) /* player found at border element */
14176 struct PlayerInfo *player = PLAYERINFO(xx, yy);
14178 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14180 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14181 continue; /* center and border element do not touch */
14184 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14185 player->index_bit, center_side);
14186 CheckTriggeredElementChangeByPlayer(x, y, center_element,
14187 CE_PLAYER_TOUCHES_X,
14188 player->index_bit, center_side);
14190 #if USE_FIX_CE_ACTION_WITH_PLAYER
14192 /* use player element that is initially defined in the level playfield,
14193 not the player element that corresponds to the runtime player number
14194 (example: a level that contains EL_PLAYER_3 as the only player would
14195 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14196 int player_element = PLAYERINFO(xx, yy)->initial_element;
14198 CheckElementChangeBySide(x, y, center_element, player_element,
14199 CE_TOUCHING_X, center_side);
14208 #if USE_ELEMENT_TOUCHING_BUGFIX
14210 void TestIfElementTouchesCustomElement(int x, int y)
14212 static int xy[4][2] =
14219 static int trigger_sides[4][2] =
14221 /* center side border side */
14222 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14223 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14224 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14225 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14227 static int touch_dir[4] =
14229 MV_LEFT | MV_RIGHT,
14234 boolean change_center_element = FALSE;
14235 int center_element = Feld[x][y]; /* should always be non-moving! */
14236 int border_element_old[NUM_DIRECTIONS];
14239 for (i = 0; i < NUM_DIRECTIONS; i++)
14241 int xx = x + xy[i][0];
14242 int yy = y + xy[i][1];
14243 int border_element;
14245 border_element_old[i] = -1;
14247 if (!IN_LEV_FIELD(xx, yy))
14250 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14251 border_element = Feld[xx][yy]; /* may be moving! */
14252 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14253 border_element = Feld[xx][yy];
14254 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14255 border_element = MovingOrBlocked2Element(xx, yy);
14257 continue; /* center and border element do not touch */
14259 border_element_old[i] = border_element;
14262 for (i = 0; i < NUM_DIRECTIONS; i++)
14264 int xx = x + xy[i][0];
14265 int yy = y + xy[i][1];
14266 int center_side = trigger_sides[i][0];
14267 int border_element = border_element_old[i];
14269 if (border_element == -1)
14272 /* check for change of border element */
14273 CheckElementChangeBySide(xx, yy, border_element, center_element,
14274 CE_TOUCHING_X, center_side);
14276 /* (center element cannot be player, so we dont have to check this here) */
14279 for (i = 0; i < NUM_DIRECTIONS; i++)
14281 int xx = x + xy[i][0];
14282 int yy = y + xy[i][1];
14283 int border_side = trigger_sides[i][1];
14284 int border_element = border_element_old[i];
14286 if (border_element == -1)
14289 /* check for change of center element (but change it only once) */
14290 if (!change_center_element)
14291 change_center_element =
14292 CheckElementChangeBySide(x, y, center_element, border_element,
14293 CE_TOUCHING_X, border_side);
14295 #if USE_FIX_CE_ACTION_WITH_PLAYER
14296 if (IS_PLAYER(xx, yy))
14298 /* use player element that is initially defined in the level playfield,
14299 not the player element that corresponds to the runtime player number
14300 (example: a level that contains EL_PLAYER_3 as the only player would
14301 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14302 int player_element = PLAYERINFO(xx, yy)->initial_element;
14304 CheckElementChangeBySide(x, y, center_element, player_element,
14305 CE_TOUCHING_X, border_side);
14313 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14315 static int xy[4][2] =
14322 static int trigger_sides[4][2] =
14324 /* center side border side */
14325 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14326 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14327 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14328 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14330 static int touch_dir[4] =
14332 MV_LEFT | MV_RIGHT,
14337 boolean change_center_element = FALSE;
14338 int center_element = Feld[x][y]; /* should always be non-moving! */
14341 for (i = 0; i < NUM_DIRECTIONS; i++)
14343 int xx = x + xy[i][0];
14344 int yy = y + xy[i][1];
14345 int center_side = trigger_sides[i][0];
14346 int border_side = trigger_sides[i][1];
14347 int border_element;
14349 if (!IN_LEV_FIELD(xx, yy))
14352 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14353 border_element = Feld[xx][yy]; /* may be moving! */
14354 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14355 border_element = Feld[xx][yy];
14356 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14357 border_element = MovingOrBlocked2Element(xx, yy);
14359 continue; /* center and border element do not touch */
14361 /* check for change of center element (but change it only once) */
14362 if (!change_center_element)
14363 change_center_element =
14364 CheckElementChangeBySide(x, y, center_element, border_element,
14365 CE_TOUCHING_X, border_side);
14367 /* check for change of border element */
14368 CheckElementChangeBySide(xx, yy, border_element, center_element,
14369 CE_TOUCHING_X, center_side);
14375 void TestIfElementHitsCustomElement(int x, int y, int direction)
14377 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14378 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14379 int hitx = x + dx, hity = y + dy;
14380 int hitting_element = Feld[x][y];
14381 int touched_element;
14383 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14386 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14387 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14389 if (IN_LEV_FIELD(hitx, hity))
14391 int opposite_direction = MV_DIR_OPPOSITE(direction);
14392 int hitting_side = direction;
14393 int touched_side = opposite_direction;
14394 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14395 MovDir[hitx][hity] != direction ||
14396 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14402 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14403 CE_HITTING_X, touched_side);
14405 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14406 CE_HIT_BY_X, hitting_side);
14408 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14409 CE_HIT_BY_SOMETHING, opposite_direction);
14411 #if USE_FIX_CE_ACTION_WITH_PLAYER
14412 if (IS_PLAYER(hitx, hity))
14414 /* use player element that is initially defined in the level playfield,
14415 not the player element that corresponds to the runtime player number
14416 (example: a level that contains EL_PLAYER_3 as the only player would
14417 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14418 int player_element = PLAYERINFO(hitx, hity)->initial_element;
14420 CheckElementChangeBySide(x, y, hitting_element, player_element,
14421 CE_HITTING_X, touched_side);
14427 /* "hitting something" is also true when hitting the playfield border */
14428 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14429 CE_HITTING_SOMETHING, direction);
14433 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14435 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14436 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14437 int hitx = x + dx, hity = y + dy;
14438 int hitting_element = Feld[x][y];
14439 int touched_element;
14441 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14442 !IS_FREE(hitx, hity) &&
14443 (!IS_MOVING(hitx, hity) ||
14444 MovDir[hitx][hity] != direction ||
14445 ABS(MovPos[hitx][hity]) <= TILEY / 2));
14448 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14452 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14456 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14457 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14459 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14460 EP_CAN_SMASH_EVERYTHING, direction);
14462 if (IN_LEV_FIELD(hitx, hity))
14464 int opposite_direction = MV_DIR_OPPOSITE(direction);
14465 int hitting_side = direction;
14466 int touched_side = opposite_direction;
14468 int touched_element = MovingOrBlocked2Element(hitx, hity);
14471 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14472 MovDir[hitx][hity] != direction ||
14473 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14482 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14483 CE_SMASHED_BY_SOMETHING, opposite_direction);
14485 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14486 CE_OTHER_IS_SMASHING, touched_side);
14488 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14489 CE_OTHER_GETS_SMASHED, hitting_side);
14495 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14497 int i, kill_x = -1, kill_y = -1;
14499 int bad_element = -1;
14500 static int test_xy[4][2] =
14507 static int test_dir[4] =
14515 for (i = 0; i < NUM_DIRECTIONS; i++)
14517 int test_x, test_y, test_move_dir, test_element;
14519 test_x = good_x + test_xy[i][0];
14520 test_y = good_y + test_xy[i][1];
14522 if (!IN_LEV_FIELD(test_x, test_y))
14526 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14528 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14530 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14531 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14533 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14534 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
14538 bad_element = test_element;
14544 if (kill_x != -1 || kill_y != -1)
14546 if (IS_PLAYER(good_x, good_y))
14548 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14550 if (player->shield_deadly_time_left > 0 &&
14551 !IS_INDESTRUCTIBLE(bad_element))
14552 Bang(kill_x, kill_y);
14553 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14554 KillPlayer(player);
14557 Bang(good_x, good_y);
14561 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14563 int i, kill_x = -1, kill_y = -1;
14564 int bad_element = Feld[bad_x][bad_y];
14565 static int test_xy[4][2] =
14572 static int touch_dir[4] =
14574 MV_LEFT | MV_RIGHT,
14579 static int test_dir[4] =
14587 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
14590 for (i = 0; i < NUM_DIRECTIONS; i++)
14592 int test_x, test_y, test_move_dir, test_element;
14594 test_x = bad_x + test_xy[i][0];
14595 test_y = bad_y + test_xy[i][1];
14597 if (!IN_LEV_FIELD(test_x, test_y))
14601 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14603 test_element = Feld[test_x][test_y];
14605 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14606 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14608 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
14609 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
14611 /* good thing is player or penguin that does not move away */
14612 if (IS_PLAYER(test_x, test_y))
14614 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14616 if (bad_element == EL_ROBOT && player->is_moving)
14617 continue; /* robot does not kill player if he is moving */
14619 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14621 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14622 continue; /* center and border element do not touch */
14630 else if (test_element == EL_PENGUIN)
14640 if (kill_x != -1 || kill_y != -1)
14642 if (IS_PLAYER(kill_x, kill_y))
14644 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14646 if (player->shield_deadly_time_left > 0 &&
14647 !IS_INDESTRUCTIBLE(bad_element))
14648 Bang(bad_x, bad_y);
14649 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14650 KillPlayer(player);
14653 Bang(kill_x, kill_y);
14657 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14659 int bad_element = Feld[bad_x][bad_y];
14660 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14661 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
14662 int test_x = bad_x + dx, test_y = bad_y + dy;
14663 int test_move_dir, test_element;
14664 int kill_x = -1, kill_y = -1;
14666 if (!IN_LEV_FIELD(test_x, test_y))
14670 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14672 test_element = Feld[test_x][test_y];
14674 if (test_move_dir != bad_move_dir)
14676 /* good thing can be player or penguin that does not move away */
14677 if (IS_PLAYER(test_x, test_y))
14679 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14681 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14682 player as being hit when he is moving towards the bad thing, because
14683 the "get hit by" condition would be lost after the player stops) */
14684 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14685 return; /* player moves away from bad thing */
14690 else if (test_element == EL_PENGUIN)
14697 if (kill_x != -1 || kill_y != -1)
14699 if (IS_PLAYER(kill_x, kill_y))
14701 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14703 if (player->shield_deadly_time_left > 0 &&
14704 !IS_INDESTRUCTIBLE(bad_element))
14705 Bang(bad_x, bad_y);
14706 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14707 KillPlayer(player);
14710 Bang(kill_x, kill_y);
14714 void TestIfPlayerTouchesBadThing(int x, int y)
14716 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14719 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14721 TestIfGoodThingHitsBadThing(x, y, move_dir);
14724 void TestIfBadThingTouchesPlayer(int x, int y)
14726 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14729 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14731 TestIfBadThingHitsGoodThing(x, y, move_dir);
14734 void TestIfFriendTouchesBadThing(int x, int y)
14736 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14739 void TestIfBadThingTouchesFriend(int x, int y)
14741 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14744 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14746 int i, kill_x = bad_x, kill_y = bad_y;
14747 static int xy[4][2] =
14755 for (i = 0; i < NUM_DIRECTIONS; i++)
14759 x = bad_x + xy[i][0];
14760 y = bad_y + xy[i][1];
14761 if (!IN_LEV_FIELD(x, y))
14764 element = Feld[x][y];
14765 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14766 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14774 if (kill_x != bad_x || kill_y != bad_y)
14775 Bang(bad_x, bad_y);
14778 void KillPlayer(struct PlayerInfo *player)
14780 int jx = player->jx, jy = player->jy;
14782 if (!player->active)
14786 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14787 player->killed, player->active, player->reanimated);
14790 /* the following code was introduced to prevent an infinite loop when calling
14792 -> CheckTriggeredElementChangeExt()
14793 -> ExecuteCustomElementAction()
14795 -> (infinitely repeating the above sequence of function calls)
14796 which occurs when killing the player while having a CE with the setting
14797 "kill player X when explosion of <player X>"; the solution using a new
14798 field "player->killed" was chosen for backwards compatibility, although
14799 clever use of the fields "player->active" etc. would probably also work */
14801 if (player->killed)
14805 player->killed = TRUE;
14807 /* remove accessible field at the player's position */
14808 Feld[jx][jy] = EL_EMPTY;
14810 /* deactivate shield (else Bang()/Explode() would not work right) */
14811 player->shield_normal_time_left = 0;
14812 player->shield_deadly_time_left = 0;
14815 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14816 player->killed, player->active, player->reanimated);
14822 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14823 player->killed, player->active, player->reanimated);
14826 #if USE_PLAYER_REANIMATION
14828 if (player->reanimated) /* killed player may have been reanimated */
14829 player->killed = player->reanimated = FALSE;
14831 BuryPlayer(player);
14833 if (player->killed) /* player may have been reanimated */
14834 BuryPlayer(player);
14837 BuryPlayer(player);
14841 static void KillPlayerUnlessEnemyProtected(int x, int y)
14843 if (!PLAYER_ENEMY_PROTECTED(x, y))
14844 KillPlayer(PLAYERINFO(x, y));
14847 static void KillPlayerUnlessExplosionProtected(int x, int y)
14849 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14850 KillPlayer(PLAYERINFO(x, y));
14853 void BuryPlayer(struct PlayerInfo *player)
14855 int jx = player->jx, jy = player->jy;
14857 if (!player->active)
14860 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14861 PlayLevelSound(jx, jy, SND_GAME_LOSING);
14863 player->GameOver = TRUE;
14864 RemovePlayer(player);
14867 void RemovePlayer(struct PlayerInfo *player)
14869 int jx = player->jx, jy = player->jy;
14870 int i, found = FALSE;
14872 player->present = FALSE;
14873 player->active = FALSE;
14875 if (!ExplodeField[jx][jy])
14876 StorePlayer[jx][jy] = 0;
14878 if (player->is_moving)
14879 TEST_DrawLevelField(player->last_jx, player->last_jy);
14881 for (i = 0; i < MAX_PLAYERS; i++)
14882 if (stored_player[i].active)
14886 AllPlayersGone = TRUE;
14892 #if USE_NEW_SNAP_DELAY
14893 static void setFieldForSnapping(int x, int y, int element, int direction)
14895 struct ElementInfo *ei = &element_info[element];
14896 int direction_bit = MV_DIR_TO_BIT(direction);
14897 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14898 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14899 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14901 Feld[x][y] = EL_ELEMENT_SNAPPING;
14902 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14904 ResetGfxAnimation(x, y);
14906 GfxElement[x][y] = element;
14907 GfxAction[x][y] = action;
14908 GfxDir[x][y] = direction;
14909 GfxFrame[x][y] = -1;
14914 =============================================================================
14915 checkDiagonalPushing()
14916 -----------------------------------------------------------------------------
14917 check if diagonal input device direction results in pushing of object
14918 (by checking if the alternative direction is walkable, diggable, ...)
14919 =============================================================================
14922 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14923 int x, int y, int real_dx, int real_dy)
14925 int jx, jy, dx, dy, xx, yy;
14927 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
14930 /* diagonal direction: check alternative direction */
14935 xx = jx + (dx == 0 ? real_dx : 0);
14936 yy = jy + (dy == 0 ? real_dy : 0);
14938 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14942 =============================================================================
14944 -----------------------------------------------------------------------------
14945 x, y: field next to player (non-diagonal) to try to dig to
14946 real_dx, real_dy: direction as read from input device (can be diagonal)
14947 =============================================================================
14950 static int DigField(struct PlayerInfo *player,
14951 int oldx, int oldy, int x, int y,
14952 int real_dx, int real_dy, int mode)
14954 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14955 boolean player_was_pushing = player->is_pushing;
14956 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14957 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14958 int jx = oldx, jy = oldy;
14959 int dx = x - jx, dy = y - jy;
14960 int nextx = x + dx, nexty = y + dy;
14961 int move_direction = (dx == -1 ? MV_LEFT :
14962 dx == +1 ? MV_RIGHT :
14964 dy == +1 ? MV_DOWN : MV_NONE);
14965 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14966 int dig_side = MV_DIR_OPPOSITE(move_direction);
14967 int old_element = Feld[jx][jy];
14968 #if USE_FIXED_DONT_RUN_INTO
14969 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14975 if (is_player) /* function can also be called by EL_PENGUIN */
14977 if (player->MovPos == 0)
14979 player->is_digging = FALSE;
14980 player->is_collecting = FALSE;
14983 if (player->MovPos == 0) /* last pushing move finished */
14984 player->is_pushing = FALSE;
14986 if (mode == DF_NO_PUSH) /* player just stopped pushing */
14988 player->is_switching = FALSE;
14989 player->push_delay = -1;
14991 return MP_NO_ACTION;
14995 #if !USE_FIXED_DONT_RUN_INTO
14996 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14997 return MP_NO_ACTION;
15000 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
15001 old_element = Back[jx][jy];
15003 /* in case of element dropped at player position, check background */
15004 else if (Back[jx][jy] != EL_EMPTY &&
15005 game.engine_version >= VERSION_IDENT(2,2,0,0))
15006 old_element = Back[jx][jy];
15008 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
15009 return MP_NO_ACTION; /* field has no opening in this direction */
15011 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
15012 return MP_NO_ACTION; /* field has no opening in this direction */
15014 #if USE_FIXED_DONT_RUN_INTO
15015 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
15019 Feld[jx][jy] = player->artwork_element;
15020 InitMovingField(jx, jy, MV_DOWN);
15021 Store[jx][jy] = EL_ACID;
15022 ContinueMoving(jx, jy);
15023 BuryPlayer(player);
15025 return MP_DONT_RUN_INTO;
15029 #if USE_FIXED_DONT_RUN_INTO
15030 if (player_can_move && DONT_RUN_INTO(element))
15032 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
15034 return MP_DONT_RUN_INTO;
15038 #if USE_FIXED_DONT_RUN_INTO
15039 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
15040 return MP_NO_ACTION;
15043 #if !USE_FIXED_DONT_RUN_INTO
15044 element = Feld[x][y];
15047 collect_count = element_info[element].collect_count_initial;
15049 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
15050 return MP_NO_ACTION;
15052 if (game.engine_version < VERSION_IDENT(2,2,0,0))
15053 player_can_move = player_can_move_or_snap;
15055 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
15056 game.engine_version >= VERSION_IDENT(2,2,0,0))
15058 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
15059 player->index_bit, dig_side);
15060 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15061 player->index_bit, dig_side);
15063 if (element == EL_DC_LANDMINE)
15066 if (Feld[x][y] != element) /* field changed by snapping */
15069 return MP_NO_ACTION;
15072 #if USE_PLAYER_GRAVITY
15073 if (player->gravity && is_player && !player->is_auto_moving &&
15074 canFallDown(player) && move_direction != MV_DOWN &&
15075 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15076 return MP_NO_ACTION; /* player cannot walk here due to gravity */
15078 if (game.gravity && is_player && !player->is_auto_moving &&
15079 canFallDown(player) && move_direction != MV_DOWN &&
15080 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15081 return MP_NO_ACTION; /* player cannot walk here due to gravity */
15084 if (player_can_move &&
15085 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
15087 int sound_element = SND_ELEMENT(element);
15088 int sound_action = ACTION_WALKING;
15090 if (IS_RND_GATE(element))
15092 if (!player->key[RND_GATE_NR(element)])
15093 return MP_NO_ACTION;
15095 else if (IS_RND_GATE_GRAY(element))
15097 if (!player->key[RND_GATE_GRAY_NR(element)])
15098 return MP_NO_ACTION;
15100 else if (IS_RND_GATE_GRAY_ACTIVE(element))
15102 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
15103 return MP_NO_ACTION;
15105 else if (element == EL_EXIT_OPEN ||
15106 element == EL_EM_EXIT_OPEN ||
15108 element == EL_EM_EXIT_OPENING ||
15110 element == EL_STEEL_EXIT_OPEN ||
15111 element == EL_EM_STEEL_EXIT_OPEN ||
15113 element == EL_EM_STEEL_EXIT_OPENING ||
15115 element == EL_SP_EXIT_OPEN ||
15116 element == EL_SP_EXIT_OPENING)
15118 sound_action = ACTION_PASSING; /* player is passing exit */
15120 else if (element == EL_EMPTY)
15122 sound_action = ACTION_MOVING; /* nothing to walk on */
15125 /* play sound from background or player, whatever is available */
15126 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
15127 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
15129 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
15131 else if (player_can_move &&
15132 IS_PASSABLE(element) && canPassField(x, y, move_direction))
15134 if (!ACCESS_FROM(element, opposite_direction))
15135 return MP_NO_ACTION; /* field not accessible from this direction */
15137 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
15138 return MP_NO_ACTION;
15140 if (IS_EM_GATE(element))
15142 if (!player->key[EM_GATE_NR(element)])
15143 return MP_NO_ACTION;
15145 else if (IS_EM_GATE_GRAY(element))
15147 if (!player->key[EM_GATE_GRAY_NR(element)])
15148 return MP_NO_ACTION;
15150 else if (IS_EM_GATE_GRAY_ACTIVE(element))
15152 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
15153 return MP_NO_ACTION;
15155 else if (IS_EMC_GATE(element))
15157 if (!player->key[EMC_GATE_NR(element)])
15158 return MP_NO_ACTION;
15160 else if (IS_EMC_GATE_GRAY(element))
15162 if (!player->key[EMC_GATE_GRAY_NR(element)])
15163 return MP_NO_ACTION;
15165 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
15167 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
15168 return MP_NO_ACTION;
15170 else if (element == EL_DC_GATE_WHITE ||
15171 element == EL_DC_GATE_WHITE_GRAY ||
15172 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15174 if (player->num_white_keys == 0)
15175 return MP_NO_ACTION;
15177 player->num_white_keys--;
15179 else if (IS_SP_PORT(element))
15181 if (element == EL_SP_GRAVITY_PORT_LEFT ||
15182 element == EL_SP_GRAVITY_PORT_RIGHT ||
15183 element == EL_SP_GRAVITY_PORT_UP ||
15184 element == EL_SP_GRAVITY_PORT_DOWN)
15185 #if USE_PLAYER_GRAVITY
15186 player->gravity = !player->gravity;
15188 game.gravity = !game.gravity;
15190 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15191 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15192 element == EL_SP_GRAVITY_ON_PORT_UP ||
15193 element == EL_SP_GRAVITY_ON_PORT_DOWN)
15194 #if USE_PLAYER_GRAVITY
15195 player->gravity = TRUE;
15197 game.gravity = TRUE;
15199 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15200 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15201 element == EL_SP_GRAVITY_OFF_PORT_UP ||
15202 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15203 #if USE_PLAYER_GRAVITY
15204 player->gravity = FALSE;
15206 game.gravity = FALSE;
15210 /* automatically move to the next field with double speed */
15211 player->programmed_action = move_direction;
15213 if (player->move_delay_reset_counter == 0)
15215 player->move_delay_reset_counter = 2; /* two double speed steps */
15217 DOUBLE_PLAYER_SPEED(player);
15220 PlayLevelSoundAction(x, y, ACTION_PASSING);
15222 else if (player_can_move_or_snap && IS_DIGGABLE(element))
15226 if (mode != DF_SNAP)
15228 GfxElement[x][y] = GFX_ELEMENT(element);
15229 player->is_digging = TRUE;
15232 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15234 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15235 player->index_bit, dig_side);
15237 if (mode == DF_SNAP)
15239 #if USE_NEW_SNAP_DELAY
15240 if (level.block_snap_field)
15241 setFieldForSnapping(x, y, element, move_direction);
15243 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15245 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15248 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15249 player->index_bit, dig_side);
15252 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15256 if (is_player && mode != DF_SNAP)
15258 GfxElement[x][y] = element;
15259 player->is_collecting = TRUE;
15262 if (element == EL_SPEED_PILL)
15264 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15266 else if (element == EL_EXTRA_TIME && level.time > 0)
15268 TimeLeft += level.extra_time;
15271 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15273 DisplayGameControlValues();
15275 DrawGameValue_Time(TimeLeft);
15278 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15280 player->shield_normal_time_left += level.shield_normal_time;
15281 if (element == EL_SHIELD_DEADLY)
15282 player->shield_deadly_time_left += level.shield_deadly_time;
15284 else if (element == EL_DYNAMITE ||
15285 element == EL_EM_DYNAMITE ||
15286 element == EL_SP_DISK_RED)
15288 if (player->inventory_size < MAX_INVENTORY_SIZE)
15289 player->inventory_element[player->inventory_size++] = element;
15291 DrawGameDoorValues();
15293 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15295 player->dynabomb_count++;
15296 player->dynabombs_left++;
15298 else if (element == EL_DYNABOMB_INCREASE_SIZE)
15300 player->dynabomb_size++;
15302 else if (element == EL_DYNABOMB_INCREASE_POWER)
15304 player->dynabomb_xl = TRUE;
15306 else if (IS_KEY(element))
15308 player->key[KEY_NR(element)] = TRUE;
15310 DrawGameDoorValues();
15312 else if (element == EL_DC_KEY_WHITE)
15314 player->num_white_keys++;
15316 /* display white keys? */
15317 /* DrawGameDoorValues(); */
15319 else if (IS_ENVELOPE(element))
15321 player->show_envelope = element;
15323 else if (element == EL_EMC_LENSES)
15325 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15327 RedrawAllInvisibleElementsForLenses();
15329 else if (element == EL_EMC_MAGNIFIER)
15331 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15333 RedrawAllInvisibleElementsForMagnifier();
15335 else if (IS_DROPPABLE(element) ||
15336 IS_THROWABLE(element)) /* can be collected and dropped */
15340 if (collect_count == 0)
15341 player->inventory_infinite_element = element;
15343 for (i = 0; i < collect_count; i++)
15344 if (player->inventory_size < MAX_INVENTORY_SIZE)
15345 player->inventory_element[player->inventory_size++] = element;
15347 DrawGameDoorValues();
15349 else if (collect_count > 0)
15351 local_player->gems_still_needed -= collect_count;
15352 if (local_player->gems_still_needed < 0)
15353 local_player->gems_still_needed = 0;
15356 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15358 DisplayGameControlValues();
15360 DrawGameValue_Emeralds(local_player->gems_still_needed);
15364 RaiseScoreElement(element);
15365 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15368 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15369 player->index_bit, dig_side);
15371 if (mode == DF_SNAP)
15373 #if USE_NEW_SNAP_DELAY
15374 if (level.block_snap_field)
15375 setFieldForSnapping(x, y, element, move_direction);
15377 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15379 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15382 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15383 player->index_bit, dig_side);
15386 else if (player_can_move_or_snap && IS_PUSHABLE(element))
15388 if (mode == DF_SNAP && element != EL_BD_ROCK)
15389 return MP_NO_ACTION;
15391 if (CAN_FALL(element) && dy)
15392 return MP_NO_ACTION;
15394 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15395 !(element == EL_SPRING && level.use_spring_bug))
15396 return MP_NO_ACTION;
15398 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15399 ((move_direction & MV_VERTICAL &&
15400 ((element_info[element].move_pattern & MV_LEFT &&
15401 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15402 (element_info[element].move_pattern & MV_RIGHT &&
15403 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15404 (move_direction & MV_HORIZONTAL &&
15405 ((element_info[element].move_pattern & MV_UP &&
15406 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15407 (element_info[element].move_pattern & MV_DOWN &&
15408 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15409 return MP_NO_ACTION;
15411 /* do not push elements already moving away faster than player */
15412 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15413 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15414 return MP_NO_ACTION;
15416 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15418 if (player->push_delay_value == -1 || !player_was_pushing)
15419 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15421 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15423 if (player->push_delay_value == -1)
15424 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15426 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15428 if (!player->is_pushing)
15429 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15432 player->is_pushing = TRUE;
15433 player->is_active = TRUE;
15435 if (!(IN_LEV_FIELD(nextx, nexty) &&
15436 (IS_FREE(nextx, nexty) ||
15437 (IS_SB_ELEMENT(element) &&
15438 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15439 (IS_CUSTOM_ELEMENT(element) &&
15440 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15441 return MP_NO_ACTION;
15443 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15444 return MP_NO_ACTION;
15446 if (player->push_delay == -1) /* new pushing; restart delay */
15447 player->push_delay = 0;
15449 if (player->push_delay < player->push_delay_value &&
15450 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15451 element != EL_SPRING && element != EL_BALLOON)
15453 /* make sure that there is no move delay before next try to push */
15454 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15455 player->move_delay = 0;
15457 return MP_NO_ACTION;
15460 if (IS_CUSTOM_ELEMENT(element) &&
15461 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15463 if (!DigFieldByCE(nextx, nexty, element))
15464 return MP_NO_ACTION;
15467 if (IS_SB_ELEMENT(element))
15469 if (element == EL_SOKOBAN_FIELD_FULL)
15471 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15472 local_player->sokobanfields_still_needed++;
15475 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15477 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15478 local_player->sokobanfields_still_needed--;
15481 Feld[x][y] = EL_SOKOBAN_OBJECT;
15483 if (Back[x][y] == Back[nextx][nexty])
15484 PlayLevelSoundAction(x, y, ACTION_PUSHING);
15485 else if (Back[x][y] != 0)
15486 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15489 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15493 if (local_player->sokobanfields_still_needed == 0 &&
15494 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
15496 if (local_player->sokobanfields_still_needed == 0 &&
15497 game.emulation == EMU_SOKOBAN)
15500 PlayerWins(player);
15502 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15506 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15508 InitMovingField(x, y, move_direction);
15509 GfxAction[x][y] = ACTION_PUSHING;
15511 if (mode == DF_SNAP)
15512 ContinueMoving(x, y);
15514 MovPos[x][y] = (dx != 0 ? dx : dy);
15516 Pushed[x][y] = TRUE;
15517 Pushed[nextx][nexty] = TRUE;
15519 if (game.engine_version < VERSION_IDENT(2,2,0,7))
15520 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15522 player->push_delay_value = -1; /* get new value later */
15524 /* check for element change _after_ element has been pushed */
15525 if (game.use_change_when_pushing_bug)
15527 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15528 player->index_bit, dig_side);
15529 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15530 player->index_bit, dig_side);
15533 else if (IS_SWITCHABLE(element))
15535 if (PLAYER_SWITCHING(player, x, y))
15537 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15538 player->index_bit, dig_side);
15543 player->is_switching = TRUE;
15544 player->switch_x = x;
15545 player->switch_y = y;
15547 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15549 if (element == EL_ROBOT_WHEEL)
15551 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15555 game.robot_wheel_active = TRUE;
15557 TEST_DrawLevelField(x, y);
15559 else if (element == EL_SP_TERMINAL)
15563 SCAN_PLAYFIELD(xx, yy)
15565 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15567 else if (Feld[xx][yy] == EL_SP_TERMINAL)
15568 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15571 else if (IS_BELT_SWITCH(element))
15573 ToggleBeltSwitch(x, y);
15575 else if (element == EL_SWITCHGATE_SWITCH_UP ||
15576 element == EL_SWITCHGATE_SWITCH_DOWN ||
15577 element == EL_DC_SWITCHGATE_SWITCH_UP ||
15578 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15580 ToggleSwitchgateSwitch(x, y);
15582 else if (element == EL_LIGHT_SWITCH ||
15583 element == EL_LIGHT_SWITCH_ACTIVE)
15585 ToggleLightSwitch(x, y);
15587 else if (element == EL_TIMEGATE_SWITCH ||
15588 element == EL_DC_TIMEGATE_SWITCH)
15590 ActivateTimegateSwitch(x, y);
15592 else if (element == EL_BALLOON_SWITCH_LEFT ||
15593 element == EL_BALLOON_SWITCH_RIGHT ||
15594 element == EL_BALLOON_SWITCH_UP ||
15595 element == EL_BALLOON_SWITCH_DOWN ||
15596 element == EL_BALLOON_SWITCH_NONE ||
15597 element == EL_BALLOON_SWITCH_ANY)
15599 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
15600 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15601 element == EL_BALLOON_SWITCH_UP ? MV_UP :
15602 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
15603 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
15606 else if (element == EL_LAMP)
15608 Feld[x][y] = EL_LAMP_ACTIVE;
15609 local_player->lights_still_needed--;
15611 ResetGfxAnimation(x, y);
15612 TEST_DrawLevelField(x, y);
15614 else if (element == EL_TIME_ORB_FULL)
15616 Feld[x][y] = EL_TIME_ORB_EMPTY;
15618 if (level.time > 0 || level.use_time_orb_bug)
15620 TimeLeft += level.time_orb_time;
15623 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15625 DisplayGameControlValues();
15627 DrawGameValue_Time(TimeLeft);
15631 ResetGfxAnimation(x, y);
15632 TEST_DrawLevelField(x, y);
15634 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15635 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15639 game.ball_state = !game.ball_state;
15641 SCAN_PLAYFIELD(xx, yy)
15643 int e = Feld[xx][yy];
15645 if (game.ball_state)
15647 if (e == EL_EMC_MAGIC_BALL)
15648 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15649 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15650 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15654 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15655 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15656 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15657 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15662 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15663 player->index_bit, dig_side);
15665 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15666 player->index_bit, dig_side);
15668 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15669 player->index_bit, dig_side);
15675 if (!PLAYER_SWITCHING(player, x, y))
15677 player->is_switching = TRUE;
15678 player->switch_x = x;
15679 player->switch_y = y;
15681 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15682 player->index_bit, dig_side);
15683 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15684 player->index_bit, dig_side);
15686 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15687 player->index_bit, dig_side);
15688 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15689 player->index_bit, dig_side);
15692 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15693 player->index_bit, dig_side);
15694 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15695 player->index_bit, dig_side);
15697 return MP_NO_ACTION;
15700 player->push_delay = -1;
15702 if (is_player) /* function can also be called by EL_PENGUIN */
15704 if (Feld[x][y] != element) /* really digged/collected something */
15706 player->is_collecting = !player->is_digging;
15707 player->is_active = TRUE;
15714 static boolean DigFieldByCE(int x, int y, int digging_element)
15716 int element = Feld[x][y];
15718 if (!IS_FREE(x, y))
15720 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15721 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15724 /* no element can dig solid indestructible elements */
15725 if (IS_INDESTRUCTIBLE(element) &&
15726 !IS_DIGGABLE(element) &&
15727 !IS_COLLECTIBLE(element))
15730 if (AmoebaNr[x][y] &&
15731 (element == EL_AMOEBA_FULL ||
15732 element == EL_BD_AMOEBA ||
15733 element == EL_AMOEBA_GROWING))
15735 AmoebaCnt[AmoebaNr[x][y]]--;
15736 AmoebaCnt2[AmoebaNr[x][y]]--;
15739 if (IS_MOVING(x, y))
15740 RemoveMovingField(x, y);
15744 TEST_DrawLevelField(x, y);
15747 /* if digged element was about to explode, prevent the explosion */
15748 ExplodeField[x][y] = EX_TYPE_NONE;
15750 PlayLevelSoundAction(x, y, action);
15753 Store[x][y] = EL_EMPTY;
15756 /* this makes it possible to leave the removed element again */
15757 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15758 Store[x][y] = element;
15760 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15762 int move_leave_element = element_info[digging_element].move_leave_element;
15764 /* this makes it possible to leave the removed element again */
15765 Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15766 element : move_leave_element);
15773 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15775 int jx = player->jx, jy = player->jy;
15776 int x = jx + dx, y = jy + dy;
15777 int snap_direction = (dx == -1 ? MV_LEFT :
15778 dx == +1 ? MV_RIGHT :
15780 dy == +1 ? MV_DOWN : MV_NONE);
15781 boolean can_continue_snapping = (level.continuous_snapping &&
15782 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15784 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15787 if (!player->active || !IN_LEV_FIELD(x, y))
15795 if (player->MovPos == 0)
15796 player->is_pushing = FALSE;
15798 player->is_snapping = FALSE;
15800 if (player->MovPos == 0)
15802 player->is_moving = FALSE;
15803 player->is_digging = FALSE;
15804 player->is_collecting = FALSE;
15810 #if USE_NEW_CONTINUOUS_SNAPPING
15811 /* prevent snapping with already pressed snap key when not allowed */
15812 if (player->is_snapping && !can_continue_snapping)
15815 if (player->is_snapping)
15819 player->MovDir = snap_direction;
15821 if (player->MovPos == 0)
15823 player->is_moving = FALSE;
15824 player->is_digging = FALSE;
15825 player->is_collecting = FALSE;
15828 player->is_dropping = FALSE;
15829 player->is_dropping_pressed = FALSE;
15830 player->drop_pressed_delay = 0;
15832 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15835 player->is_snapping = TRUE;
15836 player->is_active = TRUE;
15838 if (player->MovPos == 0)
15840 player->is_moving = FALSE;
15841 player->is_digging = FALSE;
15842 player->is_collecting = FALSE;
15845 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
15846 TEST_DrawLevelField(player->last_jx, player->last_jy);
15848 TEST_DrawLevelField(x, y);
15853 static boolean DropElement(struct PlayerInfo *player)
15855 int old_element, new_element;
15856 int dropx = player->jx, dropy = player->jy;
15857 int drop_direction = player->MovDir;
15858 int drop_side = drop_direction;
15860 int drop_element = get_next_dropped_element(player);
15862 int drop_element = (player->inventory_size > 0 ?
15863 player->inventory_element[player->inventory_size - 1] :
15864 player->inventory_infinite_element != EL_UNDEFINED ?
15865 player->inventory_infinite_element :
15866 player->dynabombs_left > 0 ?
15867 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15871 player->is_dropping_pressed = TRUE;
15873 /* do not drop an element on top of another element; when holding drop key
15874 pressed without moving, dropped element must move away before the next
15875 element can be dropped (this is especially important if the next element
15876 is dynamite, which can be placed on background for historical reasons) */
15877 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15880 if (IS_THROWABLE(drop_element))
15882 dropx += GET_DX_FROM_DIR(drop_direction);
15883 dropy += GET_DY_FROM_DIR(drop_direction);
15885 if (!IN_LEV_FIELD(dropx, dropy))
15889 old_element = Feld[dropx][dropy]; /* old element at dropping position */
15890 new_element = drop_element; /* default: no change when dropping */
15892 /* check if player is active, not moving and ready to drop */
15893 if (!player->active || player->MovPos || player->drop_delay > 0)
15896 /* check if player has anything that can be dropped */
15897 if (new_element == EL_UNDEFINED)
15900 /* check if drop key was pressed long enough for EM style dynamite */
15901 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15904 /* check if anything can be dropped at the current position */
15905 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15908 /* collected custom elements can only be dropped on empty fields */
15909 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15912 if (old_element != EL_EMPTY)
15913 Back[dropx][dropy] = old_element; /* store old element on this field */
15915 ResetGfxAnimation(dropx, dropy);
15916 ResetRandomAnimationValue(dropx, dropy);
15918 if (player->inventory_size > 0 ||
15919 player->inventory_infinite_element != EL_UNDEFINED)
15921 if (player->inventory_size > 0)
15923 player->inventory_size--;
15925 DrawGameDoorValues();
15927 if (new_element == EL_DYNAMITE)
15928 new_element = EL_DYNAMITE_ACTIVE;
15929 else if (new_element == EL_EM_DYNAMITE)
15930 new_element = EL_EM_DYNAMITE_ACTIVE;
15931 else if (new_element == EL_SP_DISK_RED)
15932 new_element = EL_SP_DISK_RED_ACTIVE;
15935 Feld[dropx][dropy] = new_element;
15937 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15938 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15939 el2img(Feld[dropx][dropy]), 0);
15941 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15943 /* needed if previous element just changed to "empty" in the last frame */
15944 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15946 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15947 player->index_bit, drop_side);
15948 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15950 player->index_bit, drop_side);
15952 TestIfElementTouchesCustomElement(dropx, dropy);
15954 else /* player is dropping a dyna bomb */
15956 player->dynabombs_left--;
15958 Feld[dropx][dropy] = new_element;
15960 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15961 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15962 el2img(Feld[dropx][dropy]), 0);
15964 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15967 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15968 InitField_WithBug1(dropx, dropy, FALSE);
15970 new_element = Feld[dropx][dropy]; /* element might have changed */
15972 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15973 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15975 int move_direction, nextx, nexty;
15977 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15978 MovDir[dropx][dropy] = drop_direction;
15980 move_direction = MovDir[dropx][dropy];
15981 nextx = dropx + GET_DX_FROM_DIR(move_direction);
15982 nexty = dropy + GET_DY_FROM_DIR(move_direction);
15984 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15986 #if USE_FIX_IMPACT_COLLISION
15987 /* do not cause impact style collision by dropping elements that can fall */
15988 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15990 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15994 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15995 player->is_dropping = TRUE;
15997 player->drop_pressed_delay = 0;
15998 player->is_dropping_pressed = FALSE;
16000 player->drop_x = dropx;
16001 player->drop_y = dropy;
16006 /* ------------------------------------------------------------------------- */
16007 /* game sound playing functions */
16008 /* ------------------------------------------------------------------------- */
16010 static int *loop_sound_frame = NULL;
16011 static int *loop_sound_volume = NULL;
16013 void InitPlayLevelSound()
16015 int num_sounds = getSoundListSize();
16017 checked_free(loop_sound_frame);
16018 checked_free(loop_sound_volume);
16020 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
16021 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
16024 static void PlayLevelSound(int x, int y, int nr)
16026 int sx = SCREENX(x), sy = SCREENY(y);
16027 int volume, stereo_position;
16028 int max_distance = 8;
16029 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
16031 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
16032 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
16035 if (!IN_LEV_FIELD(x, y) ||
16036 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
16037 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
16040 volume = SOUND_MAX_VOLUME;
16042 if (!IN_SCR_FIELD(sx, sy))
16044 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
16045 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
16047 volume -= volume * (dx > dy ? dx : dy) / max_distance;
16050 stereo_position = (SOUND_MAX_LEFT +
16051 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
16052 (SCR_FIELDX + 2 * max_distance));
16054 if (IS_LOOP_SOUND(nr))
16056 /* This assures that quieter loop sounds do not overwrite louder ones,
16057 while restarting sound volume comparison with each new game frame. */
16059 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
16062 loop_sound_volume[nr] = volume;
16063 loop_sound_frame[nr] = FrameCounter;
16066 PlaySoundExt(nr, volume, stereo_position, type);
16069 static void PlayLevelSoundNearest(int x, int y, int sound_action)
16071 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
16072 x > LEVELX(BX2) ? LEVELX(BX2) : x,
16073 y < LEVELY(BY1) ? LEVELY(BY1) :
16074 y > LEVELY(BY2) ? LEVELY(BY2) : y,
16078 static void PlayLevelSoundAction(int x, int y, int action)
16080 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
16083 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
16085 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16087 if (sound_effect != SND_UNDEFINED)
16088 PlayLevelSound(x, y, sound_effect);
16091 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
16094 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16096 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16097 PlayLevelSound(x, y, sound_effect);
16100 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
16102 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16104 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16105 PlayLevelSound(x, y, sound_effect);
16108 static void StopLevelSoundActionIfLoop(int x, int y, int action)
16110 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16112 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16113 StopSound(sound_effect);
16116 static void PlayLevelMusic()
16118 if (levelset.music[level_nr] != MUS_UNDEFINED)
16119 PlayMusic(levelset.music[level_nr]); /* from config file */
16121 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
16124 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
16126 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
16127 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
16128 int x = xx - 1 - offset;
16129 int y = yy - 1 - offset;
16134 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
16138 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16142 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16146 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16150 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16154 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16158 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16161 case SAMPLE_android_clone:
16162 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16165 case SAMPLE_android_move:
16166 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16169 case SAMPLE_spring:
16170 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16174 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16178 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16181 case SAMPLE_eater_eat:
16182 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16186 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16189 case SAMPLE_collect:
16190 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16193 case SAMPLE_diamond:
16194 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16197 case SAMPLE_squash:
16198 /* !!! CHECK THIS !!! */
16200 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16202 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16206 case SAMPLE_wonderfall:
16207 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16211 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16215 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16219 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16223 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16227 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16231 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16234 case SAMPLE_wonder:
16235 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16239 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16242 case SAMPLE_exit_open:
16243 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16246 case SAMPLE_exit_leave:
16247 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16250 case SAMPLE_dynamite:
16251 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16255 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16259 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16263 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16267 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16271 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16275 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16279 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16284 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
16286 int element = map_element_SP_to_RND(element_sp);
16287 int action = map_action_SP_to_RND(action_sp);
16288 int offset = (setup.sp_show_border_elements ? 0 : 1);
16289 int x = xx - offset;
16290 int y = yy - offset;
16293 printf("::: %d -> %d\n", element_sp, action_sp);
16296 PlayLevelSoundElementAction(x, y, element, action);
16300 void ChangeTime(int value)
16302 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
16306 /* EMC game engine uses value from time counter of RND game engine */
16307 level.native_em_level->lev->time = *time;
16309 DrawGameValue_Time(*time);
16312 void RaiseScore(int value)
16314 /* EMC game engine and RND game engine have separate score counters */
16315 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
16316 &level.native_em_level->lev->score : &local_player->score);
16320 DrawGameValue_Score(*score);
16324 void RaiseScore(int value)
16326 local_player->score += value;
16329 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16331 DisplayGameControlValues();
16333 DrawGameValue_Score(local_player->score);
16337 void RaiseScoreElement(int element)
16342 case EL_BD_DIAMOND:
16343 case EL_EMERALD_YELLOW:
16344 case EL_EMERALD_RED:
16345 case EL_EMERALD_PURPLE:
16346 case EL_SP_INFOTRON:
16347 RaiseScore(level.score[SC_EMERALD]);
16350 RaiseScore(level.score[SC_DIAMOND]);
16353 RaiseScore(level.score[SC_CRYSTAL]);
16356 RaiseScore(level.score[SC_PEARL]);
16359 case EL_BD_BUTTERFLY:
16360 case EL_SP_ELECTRON:
16361 RaiseScore(level.score[SC_BUG]);
16364 case EL_BD_FIREFLY:
16365 case EL_SP_SNIKSNAK:
16366 RaiseScore(level.score[SC_SPACESHIP]);
16369 case EL_DARK_YAMYAM:
16370 RaiseScore(level.score[SC_YAMYAM]);
16373 RaiseScore(level.score[SC_ROBOT]);
16376 RaiseScore(level.score[SC_PACMAN]);
16379 RaiseScore(level.score[SC_NUT]);
16382 case EL_EM_DYNAMITE:
16383 case EL_SP_DISK_RED:
16384 case EL_DYNABOMB_INCREASE_NUMBER:
16385 case EL_DYNABOMB_INCREASE_SIZE:
16386 case EL_DYNABOMB_INCREASE_POWER:
16387 RaiseScore(level.score[SC_DYNAMITE]);
16389 case EL_SHIELD_NORMAL:
16390 case EL_SHIELD_DEADLY:
16391 RaiseScore(level.score[SC_SHIELD]);
16393 case EL_EXTRA_TIME:
16394 RaiseScore(level.extra_time_score);
16408 case EL_DC_KEY_WHITE:
16409 RaiseScore(level.score[SC_KEY]);
16412 RaiseScore(element_info[element].collect_score);
16417 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16419 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16421 #if defined(NETWORK_AVALIABLE)
16422 if (options.network)
16423 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16432 FadeSkipNextFadeIn();
16434 fading = fading_none;
16438 OpenDoor(DOOR_CLOSE_1);
16441 game_status = GAME_MODE_MAIN;
16444 DrawAndFadeInMainMenu(REDRAW_FIELD);
16452 FadeOut(REDRAW_FIELD);
16455 game_status = GAME_MODE_MAIN;
16457 DrawAndFadeInMainMenu(REDRAW_FIELD);
16461 else /* continue playing the game */
16463 if (tape.playing && tape.deactivate_display)
16464 TapeDeactivateDisplayOff(TRUE);
16466 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16468 if (tape.playing && tape.deactivate_display)
16469 TapeDeactivateDisplayOn();
16473 void RequestQuitGame(boolean ask_if_really_quit)
16475 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16476 boolean skip_request = AllPlayersGone || quick_quit;
16478 RequestQuitGameExt(skip_request, quick_quit,
16479 "Do you really want to quit the game ?");
16483 /* ------------------------------------------------------------------------- */
16484 /* random generator functions */
16485 /* ------------------------------------------------------------------------- */
16487 unsigned int InitEngineRandom_RND(long seed)
16489 game.num_random_calls = 0;
16492 unsigned int rnd_seed = InitEngineRandom(seed);
16494 printf("::: START RND: %d\n", rnd_seed);
16499 return InitEngineRandom(seed);
16505 unsigned int RND(int max)
16509 game.num_random_calls++;
16511 return GetEngineRandom(max);
16518 /* ------------------------------------------------------------------------- */
16519 /* game engine snapshot handling functions */
16520 /* ------------------------------------------------------------------------- */
16522 struct EngineSnapshotInfo
16524 /* runtime values for custom element collect score */
16525 int collect_score[NUM_CUSTOM_ELEMENTS];
16527 /* runtime values for group element choice position */
16528 int choice_pos[NUM_GROUP_ELEMENTS];
16530 /* runtime values for belt position animations */
16531 int belt_graphic[4][NUM_BELT_PARTS];
16532 int belt_anim_mode[4][NUM_BELT_PARTS];
16535 static struct EngineSnapshotInfo engine_snapshot_rnd;
16536 static char *snapshot_level_identifier = NULL;
16537 static int snapshot_level_nr = -1;
16539 static void SaveEngineSnapshotValues_RND()
16541 static int belt_base_active_element[4] =
16543 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16544 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16545 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16546 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16550 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16552 int element = EL_CUSTOM_START + i;
16554 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16557 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16559 int element = EL_GROUP_START + i;
16561 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16564 for (i = 0; i < 4; i++)
16566 for (j = 0; j < NUM_BELT_PARTS; j++)
16568 int element = belt_base_active_element[i] + j;
16569 int graphic = el2img(element);
16570 int anim_mode = graphic_info[graphic].anim_mode;
16572 engine_snapshot_rnd.belt_graphic[i][j] = graphic;
16573 engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
16578 static void LoadEngineSnapshotValues_RND()
16580 unsigned long num_random_calls = game.num_random_calls;
16583 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16585 int element = EL_CUSTOM_START + i;
16587 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16590 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16592 int element = EL_GROUP_START + i;
16594 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16597 for (i = 0; i < 4; i++)
16599 for (j = 0; j < NUM_BELT_PARTS; j++)
16601 int graphic = engine_snapshot_rnd.belt_graphic[i][j];
16602 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
16604 graphic_info[graphic].anim_mode = anim_mode;
16608 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16610 InitRND(tape.random_seed);
16611 for (i = 0; i < num_random_calls; i++)
16615 if (game.num_random_calls != num_random_calls)
16617 Error(ERR_INFO, "number of random calls out of sync");
16618 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16619 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16620 Error(ERR_EXIT, "this should not happen -- please debug");
16624 void SaveEngineSnapshot()
16626 /* do not save snapshots from editor */
16627 if (level_editor_test_game)
16630 /* free previous snapshot buffers, if needed */
16631 FreeEngineSnapshotBuffers();
16633 /* copy some special values to a structure better suited for the snapshot */
16635 SaveEngineSnapshotValues_RND();
16636 SaveEngineSnapshotValues_EM();
16637 SaveEngineSnapshotValues_SP();
16639 /* save values stored in special snapshot structure */
16641 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16642 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16643 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16645 /* save further RND engine values */
16647 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16648 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16649 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16651 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16652 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16653 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16654 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16656 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16657 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16658 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16659 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16660 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16662 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16663 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16664 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16666 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16668 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16670 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16671 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16673 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16674 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16675 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16676 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16677 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16678 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16679 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16680 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16681 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16682 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16683 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16684 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16685 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16686 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16687 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16688 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16689 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16690 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16692 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16693 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16695 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16696 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16697 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16699 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16700 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16702 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16703 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16704 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16705 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16706 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16708 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16709 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16711 /* save level identification information */
16713 setString(&snapshot_level_identifier, leveldir_current->identifier);
16714 snapshot_level_nr = level_nr;
16717 ListNode *node = engine_snapshot_list_rnd;
16720 while (node != NULL)
16722 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16727 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16731 void LoadEngineSnapshot()
16733 /* restore generically stored snapshot buffers */
16735 LoadEngineSnapshotBuffers();
16737 /* restore special values from snapshot structure */
16739 LoadEngineSnapshotValues_RND();
16740 LoadEngineSnapshotValues_EM();
16741 LoadEngineSnapshotValues_SP();
16744 boolean CheckEngineSnapshot()
16746 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16747 snapshot_level_nr == level_nr);
16751 /* ---------- new game button stuff ---------------------------------------- */
16759 } gamebutton_info[NUM_GAME_BUTTONS] =
16762 IMG_GAME_BUTTON_GFX_STOP, &game.button.stop,
16763 GAME_CTRL_ID_STOP, "stop game"
16766 IMG_GAME_BUTTON_GFX_PAUSE, &game.button.pause,
16767 GAME_CTRL_ID_PAUSE, "pause game"
16770 IMG_GAME_BUTTON_GFX_PLAY, &game.button.play,
16771 GAME_CTRL_ID_PLAY, "play game"
16774 IMG_GAME_BUTTON_GFX_SOUND_MUSIC, &game.button.sound_music,
16775 SOUND_CTRL_ID_MUSIC, "background music on/off"
16778 IMG_GAME_BUTTON_GFX_SOUND_LOOPS, &game.button.sound_loops,
16779 SOUND_CTRL_ID_LOOPS, "sound loops on/off"
16782 IMG_GAME_BUTTON_GFX_SOUND_SIMPLE, &game.button.sound_simple,
16783 SOUND_CTRL_ID_SIMPLE, "normal sounds on/off"
16787 void CreateGameButtons()
16791 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16793 struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
16794 struct Rect *pos = gamebutton_info[i].pos;
16795 struct GadgetInfo *gi;
16798 unsigned long event_mask;
16799 int gd_x = gfx->src_x;
16800 int gd_y = gfx->src_y;
16801 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
16802 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
16803 int gd_xa = gfx->src_x + gfx->active_xoffset;
16804 int gd_ya = gfx->src_y + gfx->active_yoffset;
16805 int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16806 int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16809 if (id == GAME_CTRL_ID_STOP ||
16810 id == GAME_CTRL_ID_PAUSE ||
16811 id == GAME_CTRL_ID_PLAY)
16813 button_type = GD_TYPE_NORMAL_BUTTON;
16815 event_mask = GD_EVENT_RELEASED;
16819 button_type = GD_TYPE_CHECK_BUTTON;
16821 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16822 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16823 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16824 event_mask = GD_EVENT_PRESSED;
16827 gi = CreateGadget(GDI_CUSTOM_ID, id,
16828 GDI_INFO_TEXT, gamebutton_info[i].infotext,
16829 GDI_X, DX + pos->x,
16830 GDI_Y, DY + pos->y,
16831 GDI_WIDTH, gfx->width,
16832 GDI_HEIGHT, gfx->height,
16833 GDI_TYPE, button_type,
16834 GDI_STATE, GD_BUTTON_UNPRESSED,
16835 GDI_CHECKED, checked,
16836 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16837 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16838 GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16839 GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16840 GDI_DIRECT_DRAW, FALSE,
16841 GDI_EVENT_MASK, event_mask,
16842 GDI_CALLBACK_ACTION, HandleGameButtons,
16846 Error(ERR_EXIT, "cannot create gadget");
16848 game_gadget[id] = gi;
16852 void FreeGameButtons()
16856 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16857 FreeGadget(game_gadget[i]);
16860 static void MapGameButtons()
16864 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16865 MapGadget(game_gadget[i]);
16868 void UnmapGameButtons()
16872 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16873 UnmapGadget(game_gadget[i]);
16876 void RedrawGameButtons()
16880 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16881 RedrawGadget(game_gadget[i]);
16884 static void HandleGameButtonsExt(int id)
16886 if (game_status != GAME_MODE_PLAYING)
16891 case GAME_CTRL_ID_STOP:
16895 RequestQuitGame(TRUE);
16898 case GAME_CTRL_ID_PAUSE:
16899 if (options.network)
16901 #if defined(NETWORK_AVALIABLE)
16903 SendToServer_ContinuePlaying();
16905 SendToServer_PausePlaying();
16909 TapeTogglePause(TAPE_TOGGLE_MANUAL);
16912 case GAME_CTRL_ID_PLAY:
16915 #if defined(NETWORK_AVALIABLE)
16916 if (options.network)
16917 SendToServer_ContinuePlaying();
16921 tape.pausing = FALSE;
16922 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16927 case SOUND_CTRL_ID_MUSIC:
16928 if (setup.sound_music)
16930 setup.sound_music = FALSE;
16934 else if (audio.music_available)
16936 setup.sound = setup.sound_music = TRUE;
16938 SetAudioMode(setup.sound);
16944 case SOUND_CTRL_ID_LOOPS:
16945 if (setup.sound_loops)
16946 setup.sound_loops = FALSE;
16947 else if (audio.loops_available)
16949 setup.sound = setup.sound_loops = TRUE;
16951 SetAudioMode(setup.sound);
16955 case SOUND_CTRL_ID_SIMPLE:
16956 if (setup.sound_simple)
16957 setup.sound_simple = FALSE;
16958 else if (audio.sound_available)
16960 setup.sound = setup.sound_simple = TRUE;
16962 SetAudioMode(setup.sound);
16971 static void HandleGameButtons(struct GadgetInfo *gi)
16973 HandleGameButtonsExt(gi->custom_id);
16976 void HandleSoundButtonKeys(Key key)
16979 if (key == setup.shortcut.sound_simple)
16980 ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16981 else if (key == setup.shortcut.sound_loops)
16982 ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16983 else if (key == setup.shortcut.sound_music)
16984 ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16986 if (key == setup.shortcut.sound_simple)
16987 HandleGameButtonsExt(SOUND_CTRL_ID_SIMPLE);
16988 else if (key == setup.shortcut.sound_loops)
16989 HandleGameButtonsExt(SOUND_CTRL_ID_LOOPS);
16990 else if (key == setup.shortcut.sound_music)
16991 HandleGameButtonsExt(SOUND_CTRL_ID_MUSIC);