1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include "libgame/libgame.h"
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF ( 1)
30 #define USE_NEW_SP_SLIPPERY (USE_NEW_STUFF * 1)
31 #define USE_NEW_CUSTOM_VALUE (USE_NEW_STUFF * 1)
32 #define USE_NEW_PLAYER_ANIM (USE_NEW_STUFF * 1)
33 #define USE_NEW_ALL_SLIPPERY (USE_NEW_STUFF * 1)
34 #define USE_NEW_PLAYER_SPEED (USE_NEW_STUFF * 1)
35 #define USE_NEW_DELAYED_ACTION (USE_NEW_STUFF * 1)
36 #define USE_NEW_SNAP_DELAY (USE_NEW_STUFF * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
39 #define USE_FIXED_DONT_RUN_INTO (USE_NEW_STUFF * 1)
40 #define USE_NEW_SPRING_BUMPER (USE_NEW_STUFF * 1)
41 #define USE_STOP_CHANGED_ELEMENTS (USE_NEW_STUFF * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX (USE_NEW_STUFF * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING (USE_NEW_STUFF * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION (USE_NEW_STUFF * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES (USE_NEW_STUFF * 1)
46 #define USE_PLAYER_GRAVITY (USE_NEW_STUFF * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX (USE_NEW_STUFF * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX (USE_NEW_STUFF * 0)
50 #define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0)
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF * 1)
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX (USE_NEW_STUFF * 1)
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING (USE_NEW_STUFF * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK (USE_NEW_STUFF * 1)
59 #define USE_FIX_KILLED_BY_NON_WALKABLE (USE_NEW_STUFF * 1)
60 #define USE_FIX_IMPACT_COLLISION (USE_NEW_STUFF * 1)
61 #define USE_FIX_CE_ACTION_WITH_PLAYER (USE_NEW_STUFF * 1)
62 #define USE_FIX_NO_ACTION_AFTER_CHANGE (USE_NEW_STUFF * 1)
64 #define USE_PLAYER_REANIMATION (USE_NEW_STUFF * 1)
66 #define USE_GFX_RESET_WHEN_NOT_MOVING (USE_NEW_STUFF * 1)
68 #define USE_NEW_PLAYER_ASSIGNMENTS (USE_NEW_STUFF * 1)
70 #define USE_DELAYED_GFX_REDRAW (USE_NEW_STUFF * 0)
72 #if USE_DELAYED_GFX_REDRAW
73 #define TEST_DrawLevelField(x, y) \
74 GfxRedraw[x][y] |= GFX_REDRAW_TILE
75 #define TEST_DrawLevelFieldCrumbled(x, y) \
76 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
77 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y) \
78 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
79 #define TEST_DrawTwinkleOnField(x, y) \
80 GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
82 #define TEST_DrawLevelField(x, y) \
84 #define TEST_DrawLevelFieldCrumbled(x, y) \
85 DrawLevelFieldCrumbled(x, y)
86 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y) \
87 DrawLevelFieldCrumbledNeighbours(x, y)
88 #define TEST_DrawTwinkleOnField(x, y) \
89 DrawTwinkleOnField(x, y)
98 /* for MovePlayer() */
99 #define MP_NO_ACTION 0
102 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
104 /* for ScrollPlayer() */
105 #define SCROLL_INIT 0
106 #define SCROLL_GO_ON 1
108 /* for Bang()/Explode() */
109 #define EX_PHASE_START 0
110 #define EX_TYPE_NONE 0
111 #define EX_TYPE_NORMAL (1 << 0)
112 #define EX_TYPE_CENTER (1 << 1)
113 #define EX_TYPE_BORDER (1 << 2)
114 #define EX_TYPE_CROSS (1 << 3)
115 #define EX_TYPE_DYNA (1 << 4)
116 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
118 #define PANEL_OFF() (local_player->LevelSolved_PanelOff)
119 #define PANEL_DEACTIVATED(p) ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
120 #define PANEL_XPOS(p) (DX + ALIGNED_TEXT_XPOS(p))
121 #define PANEL_YPOS(p) (DY + ALIGNED_TEXT_YPOS(p))
123 /* special positions in the game control window (relative to control window) */
124 #define XX_LEVEL1 (PANEL_XPOS(game.panel.level))
125 #define XX_LEVEL2 (PANEL_XPOS(game.panel.level) - 1)
126 #define XX_LEVEL (PANEL_XPOS(game.panel.level))
127 #define YY_LEVEL (PANEL_YPOS(game.panel.level))
128 #define XX_EMERALDS (PANEL_XPOS(game.panel.gems))
129 #define YY_EMERALDS (PANEL_YPOS(game.panel.gems))
130 #define XX_DYNAMITE (PANEL_XPOS(game.panel.inventory))
131 #define YY_DYNAMITE (PANEL_YPOS(game.panel.inventory))
132 #define XX_KEYS (PANEL_XPOS(game.panel.keys))
133 #define YY_KEYS (PANEL_YPOS(game.panel.keys))
134 #define XX_SCORE (PANEL_XPOS(game.panel.score))
135 #define YY_SCORE (PANEL_YPOS(game.panel.score))
136 #define XX_TIME1 (PANEL_XPOS(game.panel.time))
137 #define XX_TIME2 (PANEL_XPOS(game.panel.time) + 1)
138 #define XX_TIME (PANEL_XPOS(game.panel.time))
139 #define YY_TIME (PANEL_YPOS(game.panel.time))
141 /* special positions in the game control window (relative to main window) */
142 #define DX_LEVEL1 (DX + XX_LEVEL1)
143 #define DX_LEVEL2 (DX + XX_LEVEL2)
144 #define DX_LEVEL (DX + XX_LEVEL)
145 #define DY_LEVEL (DY + YY_LEVEL)
146 #define DX_EMERALDS (DX + XX_EMERALDS)
147 #define DY_EMERALDS (DY + YY_EMERALDS)
148 #define DX_DYNAMITE (DX + XX_DYNAMITE)
149 #define DY_DYNAMITE (DY + YY_DYNAMITE)
150 #define DX_KEYS (DX + XX_KEYS)
151 #define DY_KEYS (DY + YY_KEYS)
152 #define DX_SCORE (DX + XX_SCORE)
153 #define DY_SCORE (DY + YY_SCORE)
154 #define DX_TIME1 (DX + XX_TIME1)
155 #define DX_TIME2 (DX + XX_TIME2)
156 #define DX_TIME (DX + XX_TIME)
157 #define DY_TIME (DY + YY_TIME)
160 /* game panel display and control definitions */
162 #define GAME_PANEL_LEVEL_NUMBER 0
163 #define GAME_PANEL_GEMS 1
164 #define GAME_PANEL_INVENTORY_COUNT 2
165 #define GAME_PANEL_INVENTORY_FIRST_1 3
166 #define GAME_PANEL_INVENTORY_FIRST_2 4
167 #define GAME_PANEL_INVENTORY_FIRST_3 5
168 #define GAME_PANEL_INVENTORY_FIRST_4 6
169 #define GAME_PANEL_INVENTORY_FIRST_5 7
170 #define GAME_PANEL_INVENTORY_FIRST_6 8
171 #define GAME_PANEL_INVENTORY_FIRST_7 9
172 #define GAME_PANEL_INVENTORY_FIRST_8 10
173 #define GAME_PANEL_INVENTORY_LAST_1 11
174 #define GAME_PANEL_INVENTORY_LAST_2 12
175 #define GAME_PANEL_INVENTORY_LAST_3 13
176 #define GAME_PANEL_INVENTORY_LAST_4 14
177 #define GAME_PANEL_INVENTORY_LAST_5 15
178 #define GAME_PANEL_INVENTORY_LAST_6 16
179 #define GAME_PANEL_INVENTORY_LAST_7 17
180 #define GAME_PANEL_INVENTORY_LAST_8 18
181 #define GAME_PANEL_KEY_1 19
182 #define GAME_PANEL_KEY_2 20
183 #define GAME_PANEL_KEY_3 21
184 #define GAME_PANEL_KEY_4 22
185 #define GAME_PANEL_KEY_5 23
186 #define GAME_PANEL_KEY_6 24
187 #define GAME_PANEL_KEY_7 25
188 #define GAME_PANEL_KEY_8 26
189 #define GAME_PANEL_KEY_WHITE 27
190 #define GAME_PANEL_KEY_WHITE_COUNT 28
191 #define GAME_PANEL_SCORE 29
192 #define GAME_PANEL_HIGHSCORE 30
193 #define GAME_PANEL_TIME 31
194 #define GAME_PANEL_TIME_HH 32
195 #define GAME_PANEL_TIME_MM 33
196 #define GAME_PANEL_TIME_SS 34
197 #define GAME_PANEL_FRAME 35
198 #define GAME_PANEL_SHIELD_NORMAL 36
199 #define GAME_PANEL_SHIELD_NORMAL_TIME 37
200 #define GAME_PANEL_SHIELD_DEADLY 38
201 #define GAME_PANEL_SHIELD_DEADLY_TIME 39
202 #define GAME_PANEL_EXIT 40
203 #define GAME_PANEL_EMC_MAGIC_BALL 41
204 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH 42
205 #define GAME_PANEL_LIGHT_SWITCH 43
206 #define GAME_PANEL_LIGHT_SWITCH_TIME 44
207 #define GAME_PANEL_TIMEGATE_SWITCH 45
208 #define GAME_PANEL_TIMEGATE_SWITCH_TIME 46
209 #define GAME_PANEL_SWITCHGATE_SWITCH 47
210 #define GAME_PANEL_EMC_LENSES 48
211 #define GAME_PANEL_EMC_LENSES_TIME 49
212 #define GAME_PANEL_EMC_MAGNIFIER 50
213 #define GAME_PANEL_EMC_MAGNIFIER_TIME 51
214 #define GAME_PANEL_BALLOON_SWITCH 52
215 #define GAME_PANEL_DYNABOMB_NUMBER 53
216 #define GAME_PANEL_DYNABOMB_SIZE 54
217 #define GAME_PANEL_DYNABOMB_POWER 55
218 #define GAME_PANEL_PENGUINS 56
219 #define GAME_PANEL_SOKOBAN_OBJECTS 57
220 #define GAME_PANEL_SOKOBAN_FIELDS 58
221 #define GAME_PANEL_ROBOT_WHEEL 59
222 #define GAME_PANEL_CONVEYOR_BELT_1 60
223 #define GAME_PANEL_CONVEYOR_BELT_2 61
224 #define GAME_PANEL_CONVEYOR_BELT_3 62
225 #define GAME_PANEL_CONVEYOR_BELT_4 63
226 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH 64
227 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH 65
228 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH 66
229 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH 67
230 #define GAME_PANEL_MAGIC_WALL 68
231 #define GAME_PANEL_MAGIC_WALL_TIME 69
232 #define GAME_PANEL_GRAVITY_STATE 70
233 #define GAME_PANEL_GRAPHIC_1 71
234 #define GAME_PANEL_GRAPHIC_2 72
235 #define GAME_PANEL_GRAPHIC_3 73
236 #define GAME_PANEL_GRAPHIC_4 74
237 #define GAME_PANEL_GRAPHIC_5 75
238 #define GAME_PANEL_GRAPHIC_6 76
239 #define GAME_PANEL_GRAPHIC_7 77
240 #define GAME_PANEL_GRAPHIC_8 78
241 #define GAME_PANEL_ELEMENT_1 79
242 #define GAME_PANEL_ELEMENT_2 80
243 #define GAME_PANEL_ELEMENT_3 81
244 #define GAME_PANEL_ELEMENT_4 82
245 #define GAME_PANEL_ELEMENT_5 83
246 #define GAME_PANEL_ELEMENT_6 84
247 #define GAME_PANEL_ELEMENT_7 85
248 #define GAME_PANEL_ELEMENT_8 86
249 #define GAME_PANEL_ELEMENT_COUNT_1 87
250 #define GAME_PANEL_ELEMENT_COUNT_2 88
251 #define GAME_PANEL_ELEMENT_COUNT_3 89
252 #define GAME_PANEL_ELEMENT_COUNT_4 90
253 #define GAME_PANEL_ELEMENT_COUNT_5 91
254 #define GAME_PANEL_ELEMENT_COUNT_6 92
255 #define GAME_PANEL_ELEMENT_COUNT_7 93
256 #define GAME_PANEL_ELEMENT_COUNT_8 94
257 #define GAME_PANEL_CE_SCORE_1 95
258 #define GAME_PANEL_CE_SCORE_2 96
259 #define GAME_PANEL_CE_SCORE_3 97
260 #define GAME_PANEL_CE_SCORE_4 98
261 #define GAME_PANEL_CE_SCORE_5 99
262 #define GAME_PANEL_CE_SCORE_6 100
263 #define GAME_PANEL_CE_SCORE_7 101
264 #define GAME_PANEL_CE_SCORE_8 102
265 #define GAME_PANEL_CE_SCORE_1_ELEMENT 103
266 #define GAME_PANEL_CE_SCORE_2_ELEMENT 104
267 #define GAME_PANEL_CE_SCORE_3_ELEMENT 105
268 #define GAME_PANEL_CE_SCORE_4_ELEMENT 106
269 #define GAME_PANEL_CE_SCORE_5_ELEMENT 107
270 #define GAME_PANEL_CE_SCORE_6_ELEMENT 108
271 #define GAME_PANEL_CE_SCORE_7_ELEMENT 109
272 #define GAME_PANEL_CE_SCORE_8_ELEMENT 110
273 #define GAME_PANEL_PLAYER_NAME 111
274 #define GAME_PANEL_LEVEL_NAME 112
275 #define GAME_PANEL_LEVEL_AUTHOR 113
277 #define NUM_GAME_PANEL_CONTROLS 114
279 struct GamePanelOrderInfo
285 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
287 struct GamePanelControlInfo
291 struct TextPosInfo *pos;
294 int value, last_value;
295 int frame, last_frame;
300 static struct GamePanelControlInfo game_panel_controls[] =
303 GAME_PANEL_LEVEL_NUMBER,
304 &game.panel.level_number,
313 GAME_PANEL_INVENTORY_COUNT,
314 &game.panel.inventory_count,
318 GAME_PANEL_INVENTORY_FIRST_1,
319 &game.panel.inventory_first[0],
323 GAME_PANEL_INVENTORY_FIRST_2,
324 &game.panel.inventory_first[1],
328 GAME_PANEL_INVENTORY_FIRST_3,
329 &game.panel.inventory_first[2],
333 GAME_PANEL_INVENTORY_FIRST_4,
334 &game.panel.inventory_first[3],
338 GAME_PANEL_INVENTORY_FIRST_5,
339 &game.panel.inventory_first[4],
343 GAME_PANEL_INVENTORY_FIRST_6,
344 &game.panel.inventory_first[5],
348 GAME_PANEL_INVENTORY_FIRST_7,
349 &game.panel.inventory_first[6],
353 GAME_PANEL_INVENTORY_FIRST_8,
354 &game.panel.inventory_first[7],
358 GAME_PANEL_INVENTORY_LAST_1,
359 &game.panel.inventory_last[0],
363 GAME_PANEL_INVENTORY_LAST_2,
364 &game.panel.inventory_last[1],
368 GAME_PANEL_INVENTORY_LAST_3,
369 &game.panel.inventory_last[2],
373 GAME_PANEL_INVENTORY_LAST_4,
374 &game.panel.inventory_last[3],
378 GAME_PANEL_INVENTORY_LAST_5,
379 &game.panel.inventory_last[4],
383 GAME_PANEL_INVENTORY_LAST_6,
384 &game.panel.inventory_last[5],
388 GAME_PANEL_INVENTORY_LAST_7,
389 &game.panel.inventory_last[6],
393 GAME_PANEL_INVENTORY_LAST_8,
394 &game.panel.inventory_last[7],
438 GAME_PANEL_KEY_WHITE,
439 &game.panel.key_white,
443 GAME_PANEL_KEY_WHITE_COUNT,
444 &game.panel.key_white_count,
453 GAME_PANEL_HIGHSCORE,
454 &game.panel.highscore,
483 GAME_PANEL_SHIELD_NORMAL,
484 &game.panel.shield_normal,
488 GAME_PANEL_SHIELD_NORMAL_TIME,
489 &game.panel.shield_normal_time,
493 GAME_PANEL_SHIELD_DEADLY,
494 &game.panel.shield_deadly,
498 GAME_PANEL_SHIELD_DEADLY_TIME,
499 &game.panel.shield_deadly_time,
508 GAME_PANEL_EMC_MAGIC_BALL,
509 &game.panel.emc_magic_ball,
513 GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
514 &game.panel.emc_magic_ball_switch,
518 GAME_PANEL_LIGHT_SWITCH,
519 &game.panel.light_switch,
523 GAME_PANEL_LIGHT_SWITCH_TIME,
524 &game.panel.light_switch_time,
528 GAME_PANEL_TIMEGATE_SWITCH,
529 &game.panel.timegate_switch,
533 GAME_PANEL_TIMEGATE_SWITCH_TIME,
534 &game.panel.timegate_switch_time,
538 GAME_PANEL_SWITCHGATE_SWITCH,
539 &game.panel.switchgate_switch,
543 GAME_PANEL_EMC_LENSES,
544 &game.panel.emc_lenses,
548 GAME_PANEL_EMC_LENSES_TIME,
549 &game.panel.emc_lenses_time,
553 GAME_PANEL_EMC_MAGNIFIER,
554 &game.panel.emc_magnifier,
558 GAME_PANEL_EMC_MAGNIFIER_TIME,
559 &game.panel.emc_magnifier_time,
563 GAME_PANEL_BALLOON_SWITCH,
564 &game.panel.balloon_switch,
568 GAME_PANEL_DYNABOMB_NUMBER,
569 &game.panel.dynabomb_number,
573 GAME_PANEL_DYNABOMB_SIZE,
574 &game.panel.dynabomb_size,
578 GAME_PANEL_DYNABOMB_POWER,
579 &game.panel.dynabomb_power,
584 &game.panel.penguins,
588 GAME_PANEL_SOKOBAN_OBJECTS,
589 &game.panel.sokoban_objects,
593 GAME_PANEL_SOKOBAN_FIELDS,
594 &game.panel.sokoban_fields,
598 GAME_PANEL_ROBOT_WHEEL,
599 &game.panel.robot_wheel,
603 GAME_PANEL_CONVEYOR_BELT_1,
604 &game.panel.conveyor_belt[0],
608 GAME_PANEL_CONVEYOR_BELT_2,
609 &game.panel.conveyor_belt[1],
613 GAME_PANEL_CONVEYOR_BELT_3,
614 &game.panel.conveyor_belt[2],
618 GAME_PANEL_CONVEYOR_BELT_4,
619 &game.panel.conveyor_belt[3],
623 GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
624 &game.panel.conveyor_belt_switch[0],
628 GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
629 &game.panel.conveyor_belt_switch[1],
633 GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
634 &game.panel.conveyor_belt_switch[2],
638 GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
639 &game.panel.conveyor_belt_switch[3],
643 GAME_PANEL_MAGIC_WALL,
644 &game.panel.magic_wall,
648 GAME_PANEL_MAGIC_WALL_TIME,
649 &game.panel.magic_wall_time,
653 GAME_PANEL_GRAVITY_STATE,
654 &game.panel.gravity_state,
658 GAME_PANEL_GRAPHIC_1,
659 &game.panel.graphic[0],
663 GAME_PANEL_GRAPHIC_2,
664 &game.panel.graphic[1],
668 GAME_PANEL_GRAPHIC_3,
669 &game.panel.graphic[2],
673 GAME_PANEL_GRAPHIC_4,
674 &game.panel.graphic[3],
678 GAME_PANEL_GRAPHIC_5,
679 &game.panel.graphic[4],
683 GAME_PANEL_GRAPHIC_6,
684 &game.panel.graphic[5],
688 GAME_PANEL_GRAPHIC_7,
689 &game.panel.graphic[6],
693 GAME_PANEL_GRAPHIC_8,
694 &game.panel.graphic[7],
698 GAME_PANEL_ELEMENT_1,
699 &game.panel.element[0],
703 GAME_PANEL_ELEMENT_2,
704 &game.panel.element[1],
708 GAME_PANEL_ELEMENT_3,
709 &game.panel.element[2],
713 GAME_PANEL_ELEMENT_4,
714 &game.panel.element[3],
718 GAME_PANEL_ELEMENT_5,
719 &game.panel.element[4],
723 GAME_PANEL_ELEMENT_6,
724 &game.panel.element[5],
728 GAME_PANEL_ELEMENT_7,
729 &game.panel.element[6],
733 GAME_PANEL_ELEMENT_8,
734 &game.panel.element[7],
738 GAME_PANEL_ELEMENT_COUNT_1,
739 &game.panel.element_count[0],
743 GAME_PANEL_ELEMENT_COUNT_2,
744 &game.panel.element_count[1],
748 GAME_PANEL_ELEMENT_COUNT_3,
749 &game.panel.element_count[2],
753 GAME_PANEL_ELEMENT_COUNT_4,
754 &game.panel.element_count[3],
758 GAME_PANEL_ELEMENT_COUNT_5,
759 &game.panel.element_count[4],
763 GAME_PANEL_ELEMENT_COUNT_6,
764 &game.panel.element_count[5],
768 GAME_PANEL_ELEMENT_COUNT_7,
769 &game.panel.element_count[6],
773 GAME_PANEL_ELEMENT_COUNT_8,
774 &game.panel.element_count[7],
778 GAME_PANEL_CE_SCORE_1,
779 &game.panel.ce_score[0],
783 GAME_PANEL_CE_SCORE_2,
784 &game.panel.ce_score[1],
788 GAME_PANEL_CE_SCORE_3,
789 &game.panel.ce_score[2],
793 GAME_PANEL_CE_SCORE_4,
794 &game.panel.ce_score[3],
798 GAME_PANEL_CE_SCORE_5,
799 &game.panel.ce_score[4],
803 GAME_PANEL_CE_SCORE_6,
804 &game.panel.ce_score[5],
808 GAME_PANEL_CE_SCORE_7,
809 &game.panel.ce_score[6],
813 GAME_PANEL_CE_SCORE_8,
814 &game.panel.ce_score[7],
818 GAME_PANEL_CE_SCORE_1_ELEMENT,
819 &game.panel.ce_score_element[0],
823 GAME_PANEL_CE_SCORE_2_ELEMENT,
824 &game.panel.ce_score_element[1],
828 GAME_PANEL_CE_SCORE_3_ELEMENT,
829 &game.panel.ce_score_element[2],
833 GAME_PANEL_CE_SCORE_4_ELEMENT,
834 &game.panel.ce_score_element[3],
838 GAME_PANEL_CE_SCORE_5_ELEMENT,
839 &game.panel.ce_score_element[4],
843 GAME_PANEL_CE_SCORE_6_ELEMENT,
844 &game.panel.ce_score_element[5],
848 GAME_PANEL_CE_SCORE_7_ELEMENT,
849 &game.panel.ce_score_element[6],
853 GAME_PANEL_CE_SCORE_8_ELEMENT,
854 &game.panel.ce_score_element[7],
858 GAME_PANEL_PLAYER_NAME,
859 &game.panel.player_name,
863 GAME_PANEL_LEVEL_NAME,
864 &game.panel.level_name,
868 GAME_PANEL_LEVEL_AUTHOR,
869 &game.panel.level_author,
882 /* values for delayed check of falling and moving elements and for collision */
883 #define CHECK_DELAY_MOVING 3
884 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
885 #define CHECK_DELAY_COLLISION 2
886 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
888 /* values for initial player move delay (initial delay counter value) */
889 #define INITIAL_MOVE_DELAY_OFF -1
890 #define INITIAL_MOVE_DELAY_ON 0
892 /* values for player movement speed (which is in fact a delay value) */
893 #define MOVE_DELAY_MIN_SPEED 32
894 #define MOVE_DELAY_NORMAL_SPEED 8
895 #define MOVE_DELAY_HIGH_SPEED 4
896 #define MOVE_DELAY_MAX_SPEED 1
898 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
899 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
901 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
902 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
904 /* values for other actions */
905 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
906 #define MOVE_STEPSIZE_MIN (1)
907 #define MOVE_STEPSIZE_MAX (TILEX)
909 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
910 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
912 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
914 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
915 RND(element_info[e].push_delay_random))
916 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
917 RND(element_info[e].drop_delay_random))
918 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
919 RND(element_info[e].move_delay_random))
920 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
921 (element_info[e].move_delay_random))
922 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
923 RND(element_info[e].ce_value_random_initial))
924 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
925 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
926 RND((c)->delay_random * (c)->delay_frames))
927 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
928 RND((c)->delay_random))
931 #define GET_VALID_RUNTIME_ELEMENT(e) \
932 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
934 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
935 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
936 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
937 (be) + (e) - EL_SELF)
939 #define GET_PLAYER_FROM_BITS(p) \
940 (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
942 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
943 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
944 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
945 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
946 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
947 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
948 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
949 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
950 RESOLVED_REFERENCE_ELEMENT(be, e) : \
953 #define CAN_GROW_INTO(e) \
954 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
956 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
957 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
960 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
961 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
962 (CAN_MOVE_INTO_ACID(e) && \
963 Feld[x][y] == EL_ACID) || \
966 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
967 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
968 (CAN_MOVE_INTO_ACID(e) && \
969 Feld[x][y] == EL_ACID) || \
972 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
973 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
975 (CAN_MOVE_INTO_ACID(e) && \
976 Feld[x][y] == EL_ACID) || \
977 (DONT_COLLIDE_WITH(e) && \
979 !PLAYER_ENEMY_PROTECTED(x, y))))
981 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
982 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
984 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
985 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
987 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
988 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
990 #define ANDROID_CAN_CLONE_FIELD(x, y) \
991 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
992 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
994 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
995 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
997 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
998 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
1000 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
1001 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
1003 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
1004 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
1006 #define PIG_CAN_ENTER_FIELD(e, x, y) \
1007 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
1009 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
1010 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
1011 Feld[x][y] == EL_EM_EXIT_OPEN || \
1012 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
1013 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
1014 IS_FOOD_PENGUIN(Feld[x][y])))
1015 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
1016 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1018 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
1019 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
1021 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
1022 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1024 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
1025 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
1026 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
1028 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
1030 #define CE_ENTER_FIELD_COND(e, x, y) \
1031 (!IS_PLAYER(x, y) && \
1032 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
1034 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
1035 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1037 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
1038 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1040 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
1041 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
1042 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
1043 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1045 /* game button identifiers */
1046 #define GAME_CTRL_ID_STOP 0
1047 #define GAME_CTRL_ID_PAUSE 1
1048 #define GAME_CTRL_ID_PLAY 2
1049 #define SOUND_CTRL_ID_MUSIC 3
1050 #define SOUND_CTRL_ID_LOOPS 4
1051 #define SOUND_CTRL_ID_SIMPLE 5
1053 #define NUM_GAME_BUTTONS 6
1056 /* forward declaration for internal use */
1058 static void CreateField(int, int, int);
1060 static void ResetGfxAnimation(int, int);
1062 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1063 static void AdvanceFrameAndPlayerCounters(int);
1065 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1066 static boolean MovePlayer(struct PlayerInfo *, int, int);
1067 static void ScrollPlayer(struct PlayerInfo *, int);
1068 static void ScrollScreen(struct PlayerInfo *, int);
1070 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1071 static boolean DigFieldByCE(int, int, int);
1072 static boolean SnapField(struct PlayerInfo *, int, int);
1073 static boolean DropElement(struct PlayerInfo *);
1075 static void InitBeltMovement(void);
1076 static void CloseAllOpenTimegates(void);
1077 static void CheckGravityMovement(struct PlayerInfo *);
1078 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1079 static void KillPlayerUnlessEnemyProtected(int, int);
1080 static void KillPlayerUnlessExplosionProtected(int, int);
1082 static void TestIfPlayerTouchesCustomElement(int, int);
1083 static void TestIfElementTouchesCustomElement(int, int);
1084 static void TestIfElementHitsCustomElement(int, int, int);
1086 static void TestIfElementSmashesCustomElement(int, int, int);
1089 static void HandleElementChange(int, int, int);
1090 static void ExecuteCustomElementAction(int, int, int, int);
1091 static boolean ChangeElement(int, int, int, int);
1093 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1094 #define CheckTriggeredElementChange(x, y, e, ev) \
1095 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1096 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
1097 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1098 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
1099 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1100 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
1101 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1103 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1104 #define CheckElementChange(x, y, e, te, ev) \
1105 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1106 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
1107 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1108 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
1109 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1111 static void PlayLevelSound(int, int, int);
1112 static void PlayLevelSoundNearest(int, int, int);
1113 static void PlayLevelSoundAction(int, int, int);
1114 static void PlayLevelSoundElementAction(int, int, int, int);
1115 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1116 static void PlayLevelSoundActionIfLoop(int, int, int);
1117 static void StopLevelSoundActionIfLoop(int, int, int);
1118 static void PlayLevelMusic();
1120 static void MapGameButtons();
1121 static void HandleGameButtons(struct GadgetInfo *);
1123 int AmoebeNachbarNr(int, int);
1124 void AmoebeUmwandeln(int, int);
1125 void ContinueMoving(int, int);
1126 void Bang(int, int);
1127 void InitMovDir(int, int);
1128 void InitAmoebaNr(int, int);
1129 int NewHiScore(void);
1131 void TestIfGoodThingHitsBadThing(int, int, int);
1132 void TestIfBadThingHitsGoodThing(int, int, int);
1133 void TestIfPlayerTouchesBadThing(int, int);
1134 void TestIfPlayerRunsIntoBadThing(int, int, int);
1135 void TestIfBadThingTouchesPlayer(int, int);
1136 void TestIfBadThingRunsIntoPlayer(int, int, int);
1137 void TestIfFriendTouchesBadThing(int, int);
1138 void TestIfBadThingTouchesFriend(int, int);
1139 void TestIfBadThingTouchesOtherBadThing(int, int);
1140 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1142 void KillPlayer(struct PlayerInfo *);
1143 void BuryPlayer(struct PlayerInfo *);
1144 void RemovePlayer(struct PlayerInfo *);
1146 static int getInvisibleActiveFromInvisibleElement(int);
1147 static int getInvisibleFromInvisibleActiveElement(int);
1149 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1151 /* for detection of endless loops, caused by custom element programming */
1152 /* (using maximal playfield width x 10 is just a rough approximation) */
1153 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
1155 #define RECURSION_LOOP_DETECTION_START(e, rc) \
1157 if (recursion_loop_detected) \
1160 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
1162 recursion_loop_detected = TRUE; \
1163 recursion_loop_element = (e); \
1166 recursion_loop_depth++; \
1169 #define RECURSION_LOOP_DETECTION_END() \
1171 recursion_loop_depth--; \
1174 static int recursion_loop_depth;
1175 static boolean recursion_loop_detected;
1176 static boolean recursion_loop_element;
1178 static int map_player_action[MAX_PLAYERS];
1181 /* ------------------------------------------------------------------------- */
1182 /* definition of elements that automatically change to other elements after */
1183 /* a specified time, eventually calling a function when changing */
1184 /* ------------------------------------------------------------------------- */
1186 /* forward declaration for changer functions */
1187 static void InitBuggyBase(int, int);
1188 static void WarnBuggyBase(int, int);
1190 static void InitTrap(int, int);
1191 static void ActivateTrap(int, int);
1192 static void ChangeActiveTrap(int, int);
1194 static void InitRobotWheel(int, int);
1195 static void RunRobotWheel(int, int);
1196 static void StopRobotWheel(int, int);
1198 static void InitTimegateWheel(int, int);
1199 static void RunTimegateWheel(int, int);
1201 static void InitMagicBallDelay(int, int);
1202 static void ActivateMagicBall(int, int);
1204 struct ChangingElementInfo
1209 void (*pre_change_function)(int x, int y);
1210 void (*change_function)(int x, int y);
1211 void (*post_change_function)(int x, int y);
1214 static struct ChangingElementInfo change_delay_list[] =
1249 EL_STEEL_EXIT_OPENING,
1257 EL_STEEL_EXIT_CLOSING,
1258 EL_STEEL_EXIT_CLOSED,
1285 EL_EM_STEEL_EXIT_OPENING,
1286 EL_EM_STEEL_EXIT_OPEN,
1293 EL_EM_STEEL_EXIT_CLOSING,
1297 EL_EM_STEEL_EXIT_CLOSED,
1321 EL_SWITCHGATE_OPENING,
1329 EL_SWITCHGATE_CLOSING,
1330 EL_SWITCHGATE_CLOSED,
1337 EL_TIMEGATE_OPENING,
1345 EL_TIMEGATE_CLOSING,
1354 EL_ACID_SPLASH_LEFT,
1362 EL_ACID_SPLASH_RIGHT,
1371 EL_SP_BUGGY_BASE_ACTIVATING,
1378 EL_SP_BUGGY_BASE_ACTIVATING,
1379 EL_SP_BUGGY_BASE_ACTIVE,
1386 EL_SP_BUGGY_BASE_ACTIVE,
1410 EL_ROBOT_WHEEL_ACTIVE,
1418 EL_TIMEGATE_SWITCH_ACTIVE,
1426 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1427 EL_DC_TIMEGATE_SWITCH,
1434 EL_EMC_MAGIC_BALL_ACTIVE,
1435 EL_EMC_MAGIC_BALL_ACTIVE,
1442 EL_EMC_SPRING_BUMPER_ACTIVE,
1443 EL_EMC_SPRING_BUMPER,
1450 EL_DIAGONAL_SHRINKING,
1458 EL_DIAGONAL_GROWING,
1479 int push_delay_fixed, push_delay_random;
1483 { EL_SPRING, 0, 0 },
1484 { EL_BALLOON, 0, 0 },
1486 { EL_SOKOBAN_OBJECT, 2, 0 },
1487 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1488 { EL_SATELLITE, 2, 0 },
1489 { EL_SP_DISK_YELLOW, 2, 0 },
1491 { EL_UNDEFINED, 0, 0 },
1499 move_stepsize_list[] =
1501 { EL_AMOEBA_DROP, 2 },
1502 { EL_AMOEBA_DROPPING, 2 },
1503 { EL_QUICKSAND_FILLING, 1 },
1504 { EL_QUICKSAND_EMPTYING, 1 },
1505 { EL_QUICKSAND_FAST_FILLING, 2 },
1506 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1507 { EL_MAGIC_WALL_FILLING, 2 },
1508 { EL_MAGIC_WALL_EMPTYING, 2 },
1509 { EL_BD_MAGIC_WALL_FILLING, 2 },
1510 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1511 { EL_DC_MAGIC_WALL_FILLING, 2 },
1512 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1514 { EL_UNDEFINED, 0 },
1522 collect_count_list[] =
1525 { EL_BD_DIAMOND, 1 },
1526 { EL_EMERALD_YELLOW, 1 },
1527 { EL_EMERALD_RED, 1 },
1528 { EL_EMERALD_PURPLE, 1 },
1530 { EL_SP_INFOTRON, 1 },
1534 { EL_UNDEFINED, 0 },
1542 access_direction_list[] =
1544 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1545 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1546 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1547 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1548 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1549 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1550 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1551 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1552 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1553 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1554 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1556 { EL_SP_PORT_LEFT, MV_RIGHT },
1557 { EL_SP_PORT_RIGHT, MV_LEFT },
1558 { EL_SP_PORT_UP, MV_DOWN },
1559 { EL_SP_PORT_DOWN, MV_UP },
1560 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1561 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1562 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1563 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1564 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1565 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1566 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1567 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1568 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1569 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1570 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1571 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1572 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1573 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1574 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1576 { EL_UNDEFINED, MV_NONE }
1579 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1581 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1582 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1583 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1584 IS_JUST_CHANGING(x, y))
1586 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1588 /* static variables for playfield scan mode (scanning forward or backward) */
1589 static int playfield_scan_start_x = 0;
1590 static int playfield_scan_start_y = 0;
1591 static int playfield_scan_delta_x = 1;
1592 static int playfield_scan_delta_y = 1;
1594 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1595 (y) >= 0 && (y) <= lev_fieldy - 1; \
1596 (y) += playfield_scan_delta_y) \
1597 for ((x) = playfield_scan_start_x; \
1598 (x) >= 0 && (x) <= lev_fieldx - 1; \
1599 (x) += playfield_scan_delta_x)
1602 void DEBUG_SetMaximumDynamite()
1606 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1607 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1608 local_player->inventory_element[local_player->inventory_size++] =
1613 static void InitPlayfieldScanModeVars()
1615 if (game.use_reverse_scan_direction)
1617 playfield_scan_start_x = lev_fieldx - 1;
1618 playfield_scan_start_y = lev_fieldy - 1;
1620 playfield_scan_delta_x = -1;
1621 playfield_scan_delta_y = -1;
1625 playfield_scan_start_x = 0;
1626 playfield_scan_start_y = 0;
1628 playfield_scan_delta_x = 1;
1629 playfield_scan_delta_y = 1;
1633 static void InitPlayfieldScanMode(int mode)
1635 game.use_reverse_scan_direction =
1636 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1638 InitPlayfieldScanModeVars();
1641 static int get_move_delay_from_stepsize(int move_stepsize)
1644 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1646 /* make sure that stepsize value is always a power of 2 */
1647 move_stepsize = (1 << log_2(move_stepsize));
1649 return TILEX / move_stepsize;
1652 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1655 int player_nr = player->index_nr;
1656 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1657 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1659 /* do no immediately change move delay -- the player might just be moving */
1660 player->move_delay_value_next = move_delay;
1662 /* information if player can move must be set separately */
1663 player->cannot_move = cannot_move;
1667 player->move_delay = game.initial_move_delay[player_nr];
1668 player->move_delay_value = game.initial_move_delay_value[player_nr];
1670 player->move_delay_value_next = -1;
1672 player->move_delay_reset_counter = 0;
1676 void GetPlayerConfig()
1678 GameFrameDelay = setup.game_frame_delay;
1680 if (!audio.sound_available)
1681 setup.sound_simple = FALSE;
1683 if (!audio.loops_available)
1684 setup.sound_loops = FALSE;
1686 if (!audio.music_available)
1687 setup.sound_music = FALSE;
1689 if (!video.fullscreen_available)
1690 setup.fullscreen = FALSE;
1692 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1694 SetAudioMode(setup.sound);
1698 int GetElementFromGroupElement(int element)
1700 if (IS_GROUP_ELEMENT(element))
1702 struct ElementGroupInfo *group = element_info[element].group;
1703 int last_anim_random_frame = gfx.anim_random_frame;
1706 if (group->choice_mode == ANIM_RANDOM)
1707 gfx.anim_random_frame = RND(group->num_elements_resolved);
1709 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1710 group->choice_mode, 0,
1713 if (group->choice_mode == ANIM_RANDOM)
1714 gfx.anim_random_frame = last_anim_random_frame;
1716 group->choice_pos++;
1718 element = group->element_resolved[element_pos];
1724 static void InitPlayerField(int x, int y, int element, boolean init_game)
1726 if (element == EL_SP_MURPHY)
1730 if (stored_player[0].present)
1732 Feld[x][y] = EL_SP_MURPHY_CLONE;
1738 stored_player[0].initial_element = element;
1739 stored_player[0].use_murphy = TRUE;
1741 if (!level.use_artwork_element[0])
1742 stored_player[0].artwork_element = EL_SP_MURPHY;
1745 Feld[x][y] = EL_PLAYER_1;
1751 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1752 int jx = player->jx, jy = player->jy;
1754 player->present = TRUE;
1756 player->block_last_field = (element == EL_SP_MURPHY ?
1757 level.sp_block_last_field :
1758 level.block_last_field);
1760 /* ---------- initialize player's last field block delay --------------- */
1762 /* always start with reliable default value (no adjustment needed) */
1763 player->block_delay_adjustment = 0;
1765 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1766 if (player->block_last_field && element == EL_SP_MURPHY)
1767 player->block_delay_adjustment = 1;
1769 /* special case 2: in game engines before 3.1.1, blocking was different */
1770 if (game.use_block_last_field_bug)
1771 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1773 if (!options.network || player->connected)
1775 player->active = TRUE;
1777 /* remove potentially duplicate players */
1778 if (StorePlayer[jx][jy] == Feld[x][y])
1779 StorePlayer[jx][jy] = 0;
1781 StorePlayer[x][y] = Feld[x][y];
1785 printf("Player %d activated.\n", player->element_nr);
1786 printf("[Local player is %d and currently %s.]\n",
1787 local_player->element_nr,
1788 local_player->active ? "active" : "not active");
1792 Feld[x][y] = EL_EMPTY;
1794 player->jx = player->last_jx = x;
1795 player->jy = player->last_jy = y;
1798 #if USE_PLAYER_REANIMATION
1801 int player_nr = GET_PLAYER_NR(element);
1802 struct PlayerInfo *player = &stored_player[player_nr];
1804 if (player->active && player->killed)
1805 player->reanimated = TRUE; /* if player was just killed, reanimate him */
1810 static void InitField(int x, int y, boolean init_game)
1812 int element = Feld[x][y];
1821 InitPlayerField(x, y, element, init_game);
1824 case EL_SOKOBAN_FIELD_PLAYER:
1825 element = Feld[x][y] = EL_PLAYER_1;
1826 InitField(x, y, init_game);
1828 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1829 InitField(x, y, init_game);
1832 case EL_SOKOBAN_FIELD_EMPTY:
1833 local_player->sokobanfields_still_needed++;
1837 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1838 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1839 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1840 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1841 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1842 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1843 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1844 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1845 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1846 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1855 case EL_SPACESHIP_RIGHT:
1856 case EL_SPACESHIP_UP:
1857 case EL_SPACESHIP_LEFT:
1858 case EL_SPACESHIP_DOWN:
1859 case EL_BD_BUTTERFLY:
1860 case EL_BD_BUTTERFLY_RIGHT:
1861 case EL_BD_BUTTERFLY_UP:
1862 case EL_BD_BUTTERFLY_LEFT:
1863 case EL_BD_BUTTERFLY_DOWN:
1865 case EL_BD_FIREFLY_RIGHT:
1866 case EL_BD_FIREFLY_UP:
1867 case EL_BD_FIREFLY_LEFT:
1868 case EL_BD_FIREFLY_DOWN:
1869 case EL_PACMAN_RIGHT:
1871 case EL_PACMAN_LEFT:
1872 case EL_PACMAN_DOWN:
1874 case EL_YAMYAM_LEFT:
1875 case EL_YAMYAM_RIGHT:
1877 case EL_YAMYAM_DOWN:
1878 case EL_DARK_YAMYAM:
1881 case EL_SP_SNIKSNAK:
1882 case EL_SP_ELECTRON:
1891 case EL_AMOEBA_FULL:
1896 case EL_AMOEBA_DROP:
1897 if (y == lev_fieldy - 1)
1899 Feld[x][y] = EL_AMOEBA_GROWING;
1900 Store[x][y] = EL_AMOEBA_WET;
1904 case EL_DYNAMITE_ACTIVE:
1905 case EL_SP_DISK_RED_ACTIVE:
1906 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1907 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1908 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1909 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1910 MovDelay[x][y] = 96;
1913 case EL_EM_DYNAMITE_ACTIVE:
1914 MovDelay[x][y] = 32;
1918 local_player->lights_still_needed++;
1922 local_player->friends_still_needed++;
1927 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1930 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1931 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1932 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1933 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1934 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1935 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1936 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1937 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1938 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1939 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1940 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1941 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1944 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1945 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1946 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1948 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1950 game.belt_dir[belt_nr] = belt_dir;
1951 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1953 else /* more than one switch -- set it like the first switch */
1955 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1960 #if !USE_BOTH_SWITCHGATE_SWITCHES
1961 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1963 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1966 case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1968 Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1972 case EL_LIGHT_SWITCH_ACTIVE:
1974 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1977 case EL_INVISIBLE_STEELWALL:
1978 case EL_INVISIBLE_WALL:
1979 case EL_INVISIBLE_SAND:
1980 if (game.light_time_left > 0 ||
1981 game.lenses_time_left > 0)
1982 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1985 case EL_EMC_MAGIC_BALL:
1986 if (game.ball_state)
1987 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1990 case EL_EMC_MAGIC_BALL_SWITCH:
1991 if (game.ball_state)
1992 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1995 case EL_TRIGGER_PLAYER:
1996 case EL_TRIGGER_ELEMENT:
1997 case EL_TRIGGER_CE_VALUE:
1998 case EL_TRIGGER_CE_SCORE:
2000 case EL_ANY_ELEMENT:
2001 case EL_CURRENT_CE_VALUE:
2002 case EL_CURRENT_CE_SCORE:
2019 /* reference elements should not be used on the playfield */
2020 Feld[x][y] = EL_EMPTY;
2024 if (IS_CUSTOM_ELEMENT(element))
2026 if (CAN_MOVE(element))
2029 #if USE_NEW_CUSTOM_VALUE
2030 if (!element_info[element].use_last_ce_value || init_game)
2031 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2034 else if (IS_GROUP_ELEMENT(element))
2036 Feld[x][y] = GetElementFromGroupElement(element);
2038 InitField(x, y, init_game);
2045 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2048 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2050 InitField(x, y, init_game);
2052 /* not needed to call InitMovDir() -- already done by InitField()! */
2053 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2054 CAN_MOVE(Feld[x][y]))
2058 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2060 int old_element = Feld[x][y];
2062 InitField(x, y, init_game);
2064 /* not needed to call InitMovDir() -- already done by InitField()! */
2065 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2066 CAN_MOVE(old_element) &&
2067 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2070 /* this case is in fact a combination of not less than three bugs:
2071 first, it calls InitMovDir() for elements that can move, although this is
2072 already done by InitField(); then, it checks the element that was at this
2073 field _before_ the call to InitField() (which can change it); lastly, it
2074 was not called for "mole with direction" elements, which were treated as
2075 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2081 static int get_key_element_from_nr(int key_nr)
2083 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2084 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2085 EL_EM_KEY_1 : EL_KEY_1);
2087 return key_base_element + key_nr;
2090 static int get_next_dropped_element(struct PlayerInfo *player)
2092 return (player->inventory_size > 0 ?
2093 player->inventory_element[player->inventory_size - 1] :
2094 player->inventory_infinite_element != EL_UNDEFINED ?
2095 player->inventory_infinite_element :
2096 player->dynabombs_left > 0 ?
2097 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2101 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2103 /* pos >= 0: get element from bottom of the stack;
2104 pos < 0: get element from top of the stack */
2108 int min_inventory_size = -pos;
2109 int inventory_pos = player->inventory_size - min_inventory_size;
2110 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2112 return (player->inventory_size >= min_inventory_size ?
2113 player->inventory_element[inventory_pos] :
2114 player->inventory_infinite_element != EL_UNDEFINED ?
2115 player->inventory_infinite_element :
2116 player->dynabombs_left >= min_dynabombs_left ?
2117 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2122 int min_dynabombs_left = pos + 1;
2123 int min_inventory_size = pos + 1 - player->dynabombs_left;
2124 int inventory_pos = pos - player->dynabombs_left;
2126 return (player->inventory_infinite_element != EL_UNDEFINED ?
2127 player->inventory_infinite_element :
2128 player->dynabombs_left >= min_dynabombs_left ?
2129 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2130 player->inventory_size >= min_inventory_size ?
2131 player->inventory_element[inventory_pos] :
2136 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2138 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2139 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2142 if (gpo1->sort_priority != gpo2->sort_priority)
2143 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2145 compare_result = gpo1->nr - gpo2->nr;
2147 return compare_result;
2150 void InitGameControlValues()
2154 for (i = 0; game_panel_controls[i].nr != -1; i++)
2156 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2157 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2158 struct TextPosInfo *pos = gpc->pos;
2160 int type = gpc->type;
2164 Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2165 Error(ERR_EXIT, "this should not happen -- please debug");
2168 /* force update of game controls after initialization */
2169 gpc->value = gpc->last_value = -1;
2170 gpc->frame = gpc->last_frame = -1;
2171 gpc->gfx_frame = -1;
2173 /* determine panel value width for later calculation of alignment */
2174 if (type == TYPE_INTEGER || type == TYPE_STRING)
2176 pos->width = pos->size * getFontWidth(pos->font);
2177 pos->height = getFontHeight(pos->font);
2179 else if (type == TYPE_ELEMENT)
2181 pos->width = pos->size;
2182 pos->height = pos->size;
2185 /* fill structure for game panel draw order */
2187 gpo->sort_priority = pos->sort_priority;
2190 /* sort game panel controls according to sort_priority and control number */
2191 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2192 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2195 void UpdatePlayfieldElementCount()
2197 boolean use_element_count = FALSE;
2200 /* first check if it is needed at all to calculate playfield element count */
2201 for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2202 if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2203 use_element_count = TRUE;
2205 if (!use_element_count)
2208 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2209 element_info[i].element_count = 0;
2211 SCAN_PLAYFIELD(x, y)
2213 element_info[Feld[x][y]].element_count++;
2216 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2217 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2218 if (IS_IN_GROUP(j, i))
2219 element_info[EL_GROUP_START + i].element_count +=
2220 element_info[j].element_count;
2223 void UpdateGameControlValues()
2226 int time = (local_player->LevelSolved ?
2227 local_player->LevelSolved_CountingTime :
2228 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2229 level.native_em_level->lev->time :
2230 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2231 level.native_sp_level->game_sp->time_played :
2232 level.time == 0 ? TimePlayed : TimeLeft);
2233 int score = (local_player->LevelSolved ?
2234 local_player->LevelSolved_CountingScore :
2235 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2236 level.native_em_level->lev->score :
2237 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2238 level.native_sp_level->game_sp->score :
2239 local_player->score);
2240 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2241 level.native_em_level->lev->required :
2242 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2243 level.native_sp_level->game_sp->infotrons_still_needed :
2244 local_player->gems_still_needed);
2245 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2246 level.native_em_level->lev->required > 0 :
2247 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2248 level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2249 local_player->gems_still_needed > 0 ||
2250 local_player->sokobanfields_still_needed > 0 ||
2251 local_player->lights_still_needed > 0);
2253 UpdatePlayfieldElementCount();
2255 /* update game panel control values */
2257 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2258 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2260 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2261 for (i = 0; i < MAX_NUM_KEYS; i++)
2262 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2263 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2264 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2266 if (game.centered_player_nr == -1)
2268 for (i = 0; i < MAX_PLAYERS; i++)
2270 /* only one player in Supaplex game engine */
2271 if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2274 for (k = 0; k < MAX_NUM_KEYS; k++)
2276 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2278 if (level.native_em_level->ply[i]->keys & (1 << k))
2279 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2280 get_key_element_from_nr(k);
2282 else if (stored_player[i].key[k])
2283 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2284 get_key_element_from_nr(k);
2287 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2288 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2289 level.native_em_level->ply[i]->dynamite;
2290 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2291 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2292 level.native_sp_level->game_sp->red_disk_count;
2294 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2295 stored_player[i].inventory_size;
2297 if (stored_player[i].num_white_keys > 0)
2298 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2301 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2302 stored_player[i].num_white_keys;
2307 int player_nr = game.centered_player_nr;
2309 for (k = 0; k < MAX_NUM_KEYS; k++)
2311 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2313 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2314 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2315 get_key_element_from_nr(k);
2317 else if (stored_player[player_nr].key[k])
2318 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2319 get_key_element_from_nr(k);
2322 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2323 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2324 level.native_em_level->ply[player_nr]->dynamite;
2325 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2326 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2327 level.native_sp_level->game_sp->red_disk_count;
2329 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2330 stored_player[player_nr].inventory_size;
2332 if (stored_player[player_nr].num_white_keys > 0)
2333 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2335 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2336 stored_player[player_nr].num_white_keys;
2339 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2341 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2342 get_inventory_element_from_pos(local_player, i);
2343 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2344 get_inventory_element_from_pos(local_player, -i - 1);
2347 game_panel_controls[GAME_PANEL_SCORE].value = score;
2348 game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2350 game_panel_controls[GAME_PANEL_TIME].value = time;
2352 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2353 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2354 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2356 game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2358 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2359 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2361 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2362 local_player->shield_normal_time_left;
2363 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2364 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2366 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2367 local_player->shield_deadly_time_left;
2369 game_panel_controls[GAME_PANEL_EXIT].value =
2370 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2372 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2373 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2374 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2375 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2376 EL_EMC_MAGIC_BALL_SWITCH);
2378 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2379 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2380 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2381 game.light_time_left;
2383 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2384 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2385 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2386 game.timegate_time_left;
2388 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2389 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2391 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2392 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2393 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2394 game.lenses_time_left;
2396 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2397 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2398 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2399 game.magnify_time_left;
2401 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2402 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2403 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2404 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2405 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2406 EL_BALLOON_SWITCH_NONE);
2408 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2409 local_player->dynabomb_count;
2410 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2411 local_player->dynabomb_size;
2412 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2413 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2415 game_panel_controls[GAME_PANEL_PENGUINS].value =
2416 local_player->friends_still_needed;
2418 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2419 local_player->sokobanfields_still_needed;
2420 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2421 local_player->sokobanfields_still_needed;
2423 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2424 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2426 for (i = 0; i < NUM_BELTS; i++)
2428 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2429 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2430 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2431 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2432 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2435 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2436 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2437 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2438 game.magic_wall_time_left;
2440 #if USE_PLAYER_GRAVITY
2441 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2442 local_player->gravity;
2444 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2447 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2448 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2450 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2451 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2452 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2453 game.panel.element[i].id : EL_UNDEFINED);
2455 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2456 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2457 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2458 element_info[game.panel.element_count[i].id].element_count : 0);
2460 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2461 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2462 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2463 element_info[game.panel.ce_score[i].id].collect_score : 0);
2465 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2466 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2467 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2468 element_info[game.panel.ce_score_element[i].id].collect_score :
2471 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2472 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2473 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2475 /* update game panel control frames */
2477 for (i = 0; game_panel_controls[i].nr != -1; i++)
2479 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2481 if (gpc->type == TYPE_ELEMENT)
2483 if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2485 int last_anim_random_frame = gfx.anim_random_frame;
2486 int element = gpc->value;
2487 int graphic = el2panelimg(element);
2489 if (gpc->value != gpc->last_value)
2492 gpc->gfx_random = INIT_GFX_RANDOM();
2498 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2499 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2500 gpc->gfx_random = INIT_GFX_RANDOM();
2503 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2504 gfx.anim_random_frame = gpc->gfx_random;
2506 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2507 gpc->gfx_frame = element_info[element].collect_score;
2509 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2512 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2513 gfx.anim_random_frame = last_anim_random_frame;
2519 void DisplayGameControlValues()
2521 boolean redraw_panel = FALSE;
2524 for (i = 0; game_panel_controls[i].nr != -1; i++)
2526 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2528 if (PANEL_DEACTIVATED(gpc->pos))
2531 if (gpc->value == gpc->last_value &&
2532 gpc->frame == gpc->last_frame)
2535 redraw_panel = TRUE;
2541 /* copy default game door content to main double buffer */
2543 /* !!! CHECK AGAIN !!! */
2544 SetPanelBackground();
2545 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2546 DrawBackground(DX, DY, DXSIZE, DYSIZE);
2548 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2549 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2552 /* redraw game control buttons */
2554 RedrawGameButtons();
2560 game_status = GAME_MODE_PSEUDO_PANEL;
2563 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2565 for (i = 0; game_panel_controls[i].nr != -1; i++)
2569 int nr = game_panel_order[i].nr;
2570 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2572 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2575 struct TextPosInfo *pos = gpc->pos;
2576 int type = gpc->type;
2577 int value = gpc->value;
2578 int frame = gpc->frame;
2580 int last_value = gpc->last_value;
2581 int last_frame = gpc->last_frame;
2583 int size = pos->size;
2584 int font = pos->font;
2585 boolean draw_masked = pos->draw_masked;
2586 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2588 if (PANEL_DEACTIVATED(pos))
2592 if (value == last_value && frame == last_frame)
2596 gpc->last_value = value;
2597 gpc->last_frame = frame;
2600 printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2603 if (type == TYPE_INTEGER)
2605 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2606 nr == GAME_PANEL_TIME)
2608 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2610 if (use_dynamic_size) /* use dynamic number of digits */
2612 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2613 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2614 int size2 = size1 + 1;
2615 int font1 = pos->font;
2616 int font2 = pos->font_alt;
2618 size = (value < value_change ? size1 : size2);
2619 font = (value < value_change ? font1 : font2);
2622 /* clear background if value just changed its size (dynamic digits) */
2623 if ((last_value < value_change) != (value < value_change))
2625 int width1 = size1 * getFontWidth(font1);
2626 int width2 = size2 * getFontWidth(font2);
2627 int max_width = MAX(width1, width2);
2628 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2630 pos->width = max_width;
2632 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2633 max_width, max_height);
2640 /* correct text size if "digits" is zero or less */
2642 size = strlen(int2str(value, size));
2644 /* dynamically correct text alignment */
2645 pos->width = size * getFontWidth(font);
2648 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2649 int2str(value, size), font, mask_mode);
2651 else if (type == TYPE_ELEMENT)
2653 int element, graphic;
2657 int dst_x = PANEL_XPOS(pos);
2658 int dst_y = PANEL_YPOS(pos);
2661 if (value != EL_UNDEFINED && value != EL_EMPTY)
2664 graphic = el2panelimg(value);
2666 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2669 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2673 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2676 width = graphic_info[graphic].width * size / TILESIZE;
2677 height = graphic_info[graphic].height * size / TILESIZE;
2681 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2682 dst_x - src_x, dst_y - src_y);
2683 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2688 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2693 if (value == EL_UNDEFINED || value == EL_EMPTY)
2695 element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2696 graphic = el2panelimg(element);
2698 src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2699 src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2700 src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2705 graphic = el2panelimg(value);
2707 getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2710 width = graphic_info[graphic].width * size / TILESIZE;
2711 height = graphic_info[graphic].height * size / TILESIZE;
2713 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2716 else if (type == TYPE_STRING)
2718 boolean active = (value != 0);
2719 char *state_normal = "off";
2720 char *state_active = "on";
2721 char *state = (active ? state_active : state_normal);
2722 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2723 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2724 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2725 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2727 if (nr == GAME_PANEL_GRAVITY_STATE)
2729 int font1 = pos->font; /* (used for normal state) */
2730 int font2 = pos->font_alt; /* (used for active state) */
2732 int size1 = strlen(state_normal);
2733 int size2 = strlen(state_active);
2734 int width1 = size1 * getFontWidth(font1);
2735 int width2 = size2 * getFontWidth(font2);
2736 int max_width = MAX(width1, width2);
2737 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2739 pos->width = max_width;
2741 /* clear background for values that may have changed its size */
2742 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2743 max_width, max_height);
2746 font = (active ? font2 : font1);
2756 /* don't truncate output if "chars" is zero or less */
2759 /* dynamically correct text alignment */
2760 pos->width = size * getFontWidth(font);
2764 s_cut = getStringCopyN(s, size);
2766 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2767 s_cut, font, mask_mode);
2773 redraw_mask |= REDRAW_DOOR_1;
2776 game_status = GAME_MODE_PLAYING;
2779 void UpdateAndDisplayGameControlValues()
2781 if (tape.warp_forward)
2784 UpdateGameControlValues();
2785 DisplayGameControlValues();
2788 void DrawGameValue_Emeralds(int value)
2790 struct TextPosInfo *pos = &game.panel.gems;
2792 int font_nr = pos->font;
2794 int font_nr = FONT_TEXT_2;
2796 int font_width = getFontWidth(font_nr);
2797 int chars = pos->size;
2800 return; /* !!! USE NEW STUFF !!! */
2803 if (PANEL_DEACTIVATED(pos))
2806 pos->width = chars * font_width;
2808 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2811 void DrawGameValue_Dynamite(int value)
2813 struct TextPosInfo *pos = &game.panel.inventory_count;
2815 int font_nr = pos->font;
2817 int font_nr = FONT_TEXT_2;
2819 int font_width = getFontWidth(font_nr);
2820 int chars = pos->size;
2823 return; /* !!! USE NEW STUFF !!! */
2826 if (PANEL_DEACTIVATED(pos))
2829 pos->width = chars * font_width;
2831 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2834 void DrawGameValue_Score(int value)
2836 struct TextPosInfo *pos = &game.panel.score;
2838 int font_nr = pos->font;
2840 int font_nr = FONT_TEXT_2;
2842 int font_width = getFontWidth(font_nr);
2843 int chars = pos->size;
2846 return; /* !!! USE NEW STUFF !!! */
2849 if (PANEL_DEACTIVATED(pos))
2852 pos->width = chars * font_width;
2854 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2857 void DrawGameValue_Time(int value)
2859 struct TextPosInfo *pos = &game.panel.time;
2860 static int last_value = -1;
2863 int chars = pos->size;
2865 int font1_nr = pos->font;
2866 int font2_nr = pos->font_alt;
2868 int font1_nr = FONT_TEXT_2;
2869 int font2_nr = FONT_TEXT_1;
2871 int font_nr = font1_nr;
2872 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2875 return; /* !!! USE NEW STUFF !!! */
2878 if (PANEL_DEACTIVATED(pos))
2881 if (use_dynamic_chars) /* use dynamic number of chars */
2883 chars = (value < 1000 ? chars1 : chars2);
2884 font_nr = (value < 1000 ? font1_nr : font2_nr);
2887 /* clear background if value just changed its size (dynamic chars only) */
2888 if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2890 int width1 = chars1 * getFontWidth(font1_nr);
2891 int width2 = chars2 * getFontWidth(font2_nr);
2892 int max_width = MAX(width1, width2);
2893 int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2895 pos->width = max_width;
2897 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2898 max_width, max_height);
2901 pos->width = chars * getFontWidth(font_nr);
2903 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2908 void DrawGameValue_Level(int value)
2910 struct TextPosInfo *pos = &game.panel.level_number;
2913 int chars = pos->size;
2915 int font1_nr = pos->font;
2916 int font2_nr = pos->font_alt;
2918 int font1_nr = FONT_TEXT_2;
2919 int font2_nr = FONT_TEXT_1;
2921 int font_nr = font1_nr;
2922 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2925 return; /* !!! USE NEW STUFF !!! */
2928 if (PANEL_DEACTIVATED(pos))
2931 if (use_dynamic_chars) /* use dynamic number of chars */
2933 chars = (level_nr < 100 ? chars1 : chars2);
2934 font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2937 pos->width = chars * getFontWidth(font_nr);
2939 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2942 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2945 struct TextPosInfo *pos = &game.panel.keys;
2948 int base_key_graphic = EL_KEY_1;
2953 return; /* !!! USE NEW STUFF !!! */
2957 if (PANEL_DEACTIVATED(pos))
2962 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2963 base_key_graphic = EL_EM_KEY_1;
2967 pos->width = 4 * MINI_TILEX;
2971 for (i = 0; i < MAX_NUM_KEYS; i++)
2973 /* currently only 4 of 8 possible keys are displayed */
2974 for (i = 0; i < STD_NUM_KEYS; i++)
2978 struct TextPosInfo *pos = &game.panel.key[i];
2980 int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2981 int src_y = DOOR_GFX_PAGEY1 + 123;
2983 int dst_x = PANEL_XPOS(pos);
2984 int dst_y = PANEL_YPOS(pos);
2986 int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2987 int dst_y = PANEL_YPOS(pos);
2991 int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2992 level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2994 int graphic = el2edimg(element);
2998 if (PANEL_DEACTIVATED(pos))
3003 /* masked blit with tiles from half-size scaled bitmap does not work yet
3004 (no mask bitmap created for these sizes after loading and scaling) --
3005 solution: load without creating mask, scale, then create final mask */
3007 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3008 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3013 int graphic = el2edimg(base_key_graphic + i);
3018 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
3020 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
3021 dst_x - src_x, dst_y - src_y);
3022 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
3028 DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
3030 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3031 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3034 DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
3036 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3037 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3045 void DrawGameValue_Emeralds(int value)
3047 int font_nr = FONT_TEXT_2;
3048 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3050 if (PANEL_DEACTIVATED(game.panel.gems))
3053 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
3056 void DrawGameValue_Dynamite(int value)
3058 int font_nr = FONT_TEXT_2;
3059 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3061 if (PANEL_DEACTIVATED(game.panel.inventory_count))
3064 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
3067 void DrawGameValue_Score(int value)
3069 int font_nr = FONT_TEXT_2;
3070 int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
3072 if (PANEL_DEACTIVATED(game.panel.score))
3075 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
3078 void DrawGameValue_Time(int value)
3080 int font1_nr = FONT_TEXT_2;
3082 int font2_nr = FONT_TEXT_1;
3084 int font2_nr = FONT_LEVEL_NUMBER;
3086 int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
3087 int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
3089 if (PANEL_DEACTIVATED(game.panel.time))
3092 /* clear background if value just changed its size */
3093 if (value == 999 || value == 1000)
3094 ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
3097 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
3099 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
3102 void DrawGameValue_Level(int value)
3104 int font1_nr = FONT_TEXT_2;
3106 int font2_nr = FONT_TEXT_1;
3108 int font2_nr = FONT_LEVEL_NUMBER;
3111 if (PANEL_DEACTIVATED(game.panel.level))
3115 DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
3117 DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
3120 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
3122 int base_key_graphic = EL_KEY_1;
3125 if (PANEL_DEACTIVATED(game.panel.keys))
3128 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3129 base_key_graphic = EL_EM_KEY_1;
3131 /* currently only 4 of 8 possible keys are displayed */
3132 for (i = 0; i < STD_NUM_KEYS; i++)
3134 int x = XX_KEYS + i * MINI_TILEX;
3138 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3140 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3141 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3147 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3150 int key[MAX_NUM_KEYS];
3153 /* prevent EM engine from updating time/score values parallel to GameWon() */
3154 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3155 local_player->LevelSolved)
3158 for (i = 0; i < MAX_NUM_KEYS; i++)
3159 key[i] = key_bits & (1 << i);
3161 DrawGameValue_Level(level_nr);
3163 DrawGameValue_Emeralds(emeralds);
3164 DrawGameValue_Dynamite(dynamite);
3165 DrawGameValue_Score(score);
3166 DrawGameValue_Time(time);
3168 DrawGameValue_Keys(key);
3171 void UpdateGameDoorValues()
3173 UpdateGameControlValues();
3176 void DrawGameDoorValues()
3178 DisplayGameControlValues();
3181 void DrawGameDoorValues_OLD()
3183 int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
3184 int dynamite_value = 0;
3185 int score_value = (local_player->LevelSolved ? local_player->score_final :
3186 local_player->score);
3187 int gems_value = local_player->gems_still_needed;
3191 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3193 DrawGameDoorValues_EM();
3198 if (game.centered_player_nr == -1)
3200 for (i = 0; i < MAX_PLAYERS; i++)
3202 for (j = 0; j < MAX_NUM_KEYS; j++)
3203 if (stored_player[i].key[j])
3204 key_bits |= (1 << j);
3206 dynamite_value += stored_player[i].inventory_size;
3211 int player_nr = game.centered_player_nr;
3213 for (i = 0; i < MAX_NUM_KEYS; i++)
3214 if (stored_player[player_nr].key[i])
3215 key_bits |= (1 << i);
3217 dynamite_value = stored_player[player_nr].inventory_size;
3220 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3226 =============================================================================
3228 -----------------------------------------------------------------------------
3229 initialize game engine due to level / tape version number
3230 =============================================================================
3233 static void InitGameEngine()
3235 int i, j, k, l, x, y;
3237 /* set game engine from tape file when re-playing, else from level file */
3238 game.engine_version = (tape.playing ? tape.engine_version :
3239 level.game_version);
3241 /* ---------------------------------------------------------------------- */
3242 /* set flags for bugs and changes according to active game engine version */
3243 /* ---------------------------------------------------------------------- */
3246 Summary of bugfix/change:
3247 Fixed handling for custom elements that change when pushed by the player.
3249 Fixed/changed in version:
3253 Before 3.1.0, custom elements that "change when pushing" changed directly
3254 after the player started pushing them (until then handled in "DigField()").
3255 Since 3.1.0, these custom elements are not changed until the "pushing"
3256 move of the element is finished (now handled in "ContinueMoving()").
3258 Affected levels/tapes:
3259 The first condition is generally needed for all levels/tapes before version
3260 3.1.0, which might use the old behaviour before it was changed; known tapes
3261 that are affected are some tapes from the level set "Walpurgis Gardens" by
3263 The second condition is an exception from the above case and is needed for
3264 the special case of tapes recorded with game (not engine!) version 3.1.0 or
3265 above (including some development versions of 3.1.0), but before it was
3266 known that this change would break tapes like the above and was fixed in
3267 3.1.1, so that the changed behaviour was active although the engine version
3268 while recording maybe was before 3.1.0. There is at least one tape that is
3269 affected by this exception, which is the tape for the one-level set "Bug
3270 Machine" by Juergen Bonhagen.
3273 game.use_change_when_pushing_bug =
3274 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3276 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3277 tape.game_version < VERSION_IDENT(3,1,1,0)));
3280 Summary of bugfix/change:
3281 Fixed handling for blocking the field the player leaves when moving.
3283 Fixed/changed in version:
3287 Before 3.1.1, when "block last field when moving" was enabled, the field
3288 the player is leaving when moving was blocked for the time of the move,
3289 and was directly unblocked afterwards. This resulted in the last field
3290 being blocked for exactly one less than the number of frames of one player
3291 move. Additionally, even when blocking was disabled, the last field was
3292 blocked for exactly one frame.
3293 Since 3.1.1, due to changes in player movement handling, the last field
3294 is not blocked at all when blocking is disabled. When blocking is enabled,
3295 the last field is blocked for exactly the number of frames of one player
3296 move. Additionally, if the player is Murphy, the hero of Supaplex, the
3297 last field is blocked for exactly one more than the number of frames of
3300 Affected levels/tapes:
3301 (!!! yet to be determined -- probably many !!!)
3304 game.use_block_last_field_bug =
3305 (game.engine_version < VERSION_IDENT(3,1,1,0));
3308 Summary of bugfix/change:
3309 Changed behaviour of CE changes with multiple changes per single frame.
3311 Fixed/changed in version:
3315 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3316 This resulted in race conditions where CEs seem to behave strange in some
3317 situations (where triggered CE changes were just skipped because there was
3318 already a CE change on that tile in the playfield in that engine frame).
3319 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3320 (The number of changes per frame must be limited in any case, because else
3321 it is easily possible to define CE changes that would result in an infinite
3322 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3323 should be set large enough so that it would only be reached in cases where
3324 the corresponding CE change conditions run into a loop. Therefore, it seems
3325 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3326 maximal number of change pages for custom elements.)
3328 Affected levels/tapes:
3332 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3333 game.max_num_changes_per_frame = 1;
3335 game.max_num_changes_per_frame =
3336 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3339 /* ---------------------------------------------------------------------- */
3341 /* default scan direction: scan playfield from top/left to bottom/right */
3342 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3344 /* dynamically adjust element properties according to game engine version */
3345 InitElementPropertiesEngine(game.engine_version);
3348 printf("level %d: level version == %06d\n", level_nr, level.game_version);
3349 printf(" tape version == %06d [%s] [file: %06d]\n",
3350 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3352 printf(" => game.engine_version == %06d\n", game.engine_version);
3355 /* ---------- initialize player's initial move delay --------------------- */
3357 /* dynamically adjust player properties according to level information */
3358 for (i = 0; i < MAX_PLAYERS; i++)
3359 game.initial_move_delay_value[i] =
3360 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3362 /* dynamically adjust player properties according to game engine version */
3363 for (i = 0; i < MAX_PLAYERS; i++)
3364 game.initial_move_delay[i] =
3365 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3366 game.initial_move_delay_value[i] : 0);
3368 /* ---------- initialize player's initial push delay --------------------- */
3370 /* dynamically adjust player properties according to game engine version */
3371 game.initial_push_delay_value =
3372 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3374 /* ---------- initialize changing elements ------------------------------- */
3376 /* initialize changing elements information */
3377 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3379 struct ElementInfo *ei = &element_info[i];
3381 /* this pointer might have been changed in the level editor */
3382 ei->change = &ei->change_page[0];
3384 if (!IS_CUSTOM_ELEMENT(i))
3386 ei->change->target_element = EL_EMPTY_SPACE;
3387 ei->change->delay_fixed = 0;
3388 ei->change->delay_random = 0;
3389 ei->change->delay_frames = 1;
3392 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3394 ei->has_change_event[j] = FALSE;
3396 ei->event_page_nr[j] = 0;
3397 ei->event_page[j] = &ei->change_page[0];
3401 /* add changing elements from pre-defined list */
3402 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3404 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3405 struct ElementInfo *ei = &element_info[ch_delay->element];
3407 ei->change->target_element = ch_delay->target_element;
3408 ei->change->delay_fixed = ch_delay->change_delay;
3410 ei->change->pre_change_function = ch_delay->pre_change_function;
3411 ei->change->change_function = ch_delay->change_function;
3412 ei->change->post_change_function = ch_delay->post_change_function;
3414 ei->change->can_change = TRUE;
3415 ei->change->can_change_or_has_action = TRUE;
3417 ei->has_change_event[CE_DELAY] = TRUE;
3419 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3420 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3423 /* ---------- initialize internal run-time variables --------------------- */
3425 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3427 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3429 for (j = 0; j < ei->num_change_pages; j++)
3431 ei->change_page[j].can_change_or_has_action =
3432 (ei->change_page[j].can_change |
3433 ei->change_page[j].has_action);
3437 /* add change events from custom element configuration */
3438 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3440 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3442 for (j = 0; j < ei->num_change_pages; j++)
3444 if (!ei->change_page[j].can_change_or_has_action)
3447 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3449 /* only add event page for the first page found with this event */
3450 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3452 ei->has_change_event[k] = TRUE;
3454 ei->event_page_nr[k] = j;
3455 ei->event_page[k] = &ei->change_page[j];
3462 /* ---------- initialize reference elements in change conditions --------- */
3464 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3466 int element = EL_CUSTOM_START + i;
3467 struct ElementInfo *ei = &element_info[element];
3469 for (j = 0; j < ei->num_change_pages; j++)
3471 int trigger_element = ei->change_page[j].initial_trigger_element;
3473 if (trigger_element >= EL_PREV_CE_8 &&
3474 trigger_element <= EL_NEXT_CE_8)
3475 trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3477 ei->change_page[j].trigger_element = trigger_element;
3482 /* ---------- initialize run-time trigger player and element ------------- */
3484 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3486 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3488 for (j = 0; j < ei->num_change_pages; j++)
3490 ei->change_page[j].actual_trigger_element = EL_EMPTY;
3491 ei->change_page[j].actual_trigger_player = EL_EMPTY;
3492 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3493 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3494 ei->change_page[j].actual_trigger_ce_value = 0;
3495 ei->change_page[j].actual_trigger_ce_score = 0;
3499 /* ---------- initialize trigger events ---------------------------------- */
3501 /* initialize trigger events information */
3502 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3503 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3504 trigger_events[i][j] = FALSE;
3506 /* add trigger events from element change event properties */
3507 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3509 struct ElementInfo *ei = &element_info[i];
3511 for (j = 0; j < ei->num_change_pages; j++)
3513 if (!ei->change_page[j].can_change_or_has_action)
3516 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3518 int trigger_element = ei->change_page[j].trigger_element;
3520 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3522 if (ei->change_page[j].has_event[k])
3524 if (IS_GROUP_ELEMENT(trigger_element))
3526 struct ElementGroupInfo *group =
3527 element_info[trigger_element].group;
3529 for (l = 0; l < group->num_elements_resolved; l++)
3530 trigger_events[group->element_resolved[l]][k] = TRUE;
3532 else if (trigger_element == EL_ANY_ELEMENT)
3533 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3534 trigger_events[l][k] = TRUE;
3536 trigger_events[trigger_element][k] = TRUE;
3543 /* ---------- initialize push delay -------------------------------------- */
3545 /* initialize push delay values to default */
3546 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3548 if (!IS_CUSTOM_ELEMENT(i))
3550 /* set default push delay values (corrected since version 3.0.7-1) */
3551 if (game.engine_version < VERSION_IDENT(3,0,7,1))
3553 element_info[i].push_delay_fixed = 2;
3554 element_info[i].push_delay_random = 8;
3558 element_info[i].push_delay_fixed = 8;
3559 element_info[i].push_delay_random = 8;
3564 /* set push delay value for certain elements from pre-defined list */
3565 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3567 int e = push_delay_list[i].element;
3569 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
3570 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3573 /* set push delay value for Supaplex elements for newer engine versions */
3574 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3576 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3578 if (IS_SP_ELEMENT(i))
3580 /* set SP push delay to just enough to push under a falling zonk */
3581 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3583 element_info[i].push_delay_fixed = delay;
3584 element_info[i].push_delay_random = 0;
3589 /* ---------- initialize move stepsize ----------------------------------- */
3591 /* initialize move stepsize values to default */
3592 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3593 if (!IS_CUSTOM_ELEMENT(i))
3594 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3596 /* set move stepsize value for certain elements from pre-defined list */
3597 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3599 int e = move_stepsize_list[i].element;
3601 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3604 /* ---------- initialize collect score ----------------------------------- */
3606 /* initialize collect score values for custom elements from initial value */
3607 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3608 if (IS_CUSTOM_ELEMENT(i))
3609 element_info[i].collect_score = element_info[i].collect_score_initial;
3611 /* ---------- initialize collect count ----------------------------------- */
3613 /* initialize collect count values for non-custom elements */
3614 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3615 if (!IS_CUSTOM_ELEMENT(i))
3616 element_info[i].collect_count_initial = 0;
3618 /* add collect count values for all elements from pre-defined list */
3619 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3620 element_info[collect_count_list[i].element].collect_count_initial =
3621 collect_count_list[i].count;
3623 /* ---------- initialize access direction -------------------------------- */
3625 /* initialize access direction values to default (access from every side) */
3626 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3627 if (!IS_CUSTOM_ELEMENT(i))
3628 element_info[i].access_direction = MV_ALL_DIRECTIONS;
3630 /* set access direction value for certain elements from pre-defined list */
3631 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3632 element_info[access_direction_list[i].element].access_direction =
3633 access_direction_list[i].direction;
3635 /* ---------- initialize explosion content ------------------------------- */
3636 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3638 if (IS_CUSTOM_ELEMENT(i))
3641 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3643 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3645 element_info[i].content.e[x][y] =
3646 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3647 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3648 i == EL_PLAYER_3 ? EL_EMERALD :
3649 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3650 i == EL_MOLE ? EL_EMERALD_RED :
3651 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3652 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3653 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3654 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3655 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3656 i == EL_WALL_EMERALD ? EL_EMERALD :
3657 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3658 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3659 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3660 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3661 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3662 i == EL_WALL_PEARL ? EL_PEARL :
3663 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3668 /* ---------- initialize recursion detection ------------------------------ */
3669 recursion_loop_depth = 0;
3670 recursion_loop_detected = FALSE;
3671 recursion_loop_element = EL_UNDEFINED;
3673 /* ---------- initialize graphics engine ---------------------------------- */
3674 game.scroll_delay_value =
3675 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3676 setup.scroll_delay ? setup.scroll_delay_value : 0);
3677 game.scroll_delay_value =
3678 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3681 int get_num_special_action(int element, int action_first, int action_last)
3683 int num_special_action = 0;
3686 for (i = action_first; i <= action_last; i++)
3688 boolean found = FALSE;
3690 for (j = 0; j < NUM_DIRECTIONS; j++)
3691 if (el_act_dir2img(element, i, j) !=
3692 el_act_dir2img(element, ACTION_DEFAULT, j))
3696 num_special_action++;
3701 return num_special_action;
3706 =============================================================================
3708 -----------------------------------------------------------------------------
3709 initialize and start new game
3710 =============================================================================
3715 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3716 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3717 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3719 boolean do_fading = (game_status == GAME_MODE_MAIN);
3722 int initial_move_dir = MV_DOWN;
3724 int initial_move_dir = MV_NONE;
3728 game_status = GAME_MODE_PLAYING;
3731 /* needed if different viewport properties defined for playing */
3732 ChangeViewportPropertiesIfNeeded();
3736 DrawCompleteVideoDisplay();
3740 InitGameControlValues();
3742 /* don't play tapes over network */
3743 network_playing = (options.network && !tape.playing);
3745 for (i = 0; i < MAX_PLAYERS; i++)
3747 struct PlayerInfo *player = &stored_player[i];
3749 player->index_nr = i;
3750 player->index_bit = (1 << i);
3751 player->element_nr = EL_PLAYER_1 + i;
3753 player->present = FALSE;
3754 player->active = FALSE;
3755 player->mapped = FALSE;
3757 player->killed = FALSE;
3758 player->reanimated = FALSE;
3761 player->effective_action = 0;
3762 player->programmed_action = 0;
3765 player->score_final = 0;
3767 player->gems_still_needed = level.gems_needed;
3768 player->sokobanfields_still_needed = 0;
3769 player->lights_still_needed = 0;
3770 player->friends_still_needed = 0;
3772 for (j = 0; j < MAX_NUM_KEYS; j++)
3773 player->key[j] = FALSE;
3775 player->num_white_keys = 0;
3777 player->dynabomb_count = 0;
3778 player->dynabomb_size = 1;
3779 player->dynabombs_left = 0;
3780 player->dynabomb_xl = FALSE;
3782 player->MovDir = initial_move_dir;
3785 player->GfxDir = initial_move_dir;
3786 player->GfxAction = ACTION_DEFAULT;
3788 player->StepFrame = 0;
3790 player->initial_element = player->element_nr;
3791 player->artwork_element =
3792 (level.use_artwork_element[i] ? level.artwork_element[i] :
3793 player->element_nr);
3794 player->use_murphy = FALSE;
3796 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3797 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3799 player->gravity = level.initial_player_gravity[i];
3801 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3803 player->actual_frame_counter = 0;
3805 player->step_counter = 0;
3807 player->last_move_dir = initial_move_dir;
3809 player->is_active = FALSE;
3811 player->is_waiting = FALSE;
3812 player->is_moving = FALSE;
3813 player->is_auto_moving = FALSE;
3814 player->is_digging = FALSE;
3815 player->is_snapping = FALSE;
3816 player->is_collecting = FALSE;
3817 player->is_pushing = FALSE;
3818 player->is_switching = FALSE;
3819 player->is_dropping = FALSE;
3820 player->is_dropping_pressed = FALSE;
3822 player->is_bored = FALSE;
3823 player->is_sleeping = FALSE;
3825 player->frame_counter_bored = -1;
3826 player->frame_counter_sleeping = -1;
3828 player->anim_delay_counter = 0;
3829 player->post_delay_counter = 0;
3831 player->dir_waiting = initial_move_dir;
3832 player->action_waiting = ACTION_DEFAULT;
3833 player->last_action_waiting = ACTION_DEFAULT;
3834 player->special_action_bored = ACTION_DEFAULT;
3835 player->special_action_sleeping = ACTION_DEFAULT;
3837 player->switch_x = -1;
3838 player->switch_y = -1;
3840 player->drop_x = -1;
3841 player->drop_y = -1;
3843 player->show_envelope = 0;
3845 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3847 player->push_delay = -1; /* initialized when pushing starts */
3848 player->push_delay_value = game.initial_push_delay_value;
3850 player->drop_delay = 0;
3851 player->drop_pressed_delay = 0;
3853 player->last_jx = -1;
3854 player->last_jy = -1;
3858 player->shield_normal_time_left = 0;
3859 player->shield_deadly_time_left = 0;
3861 player->inventory_infinite_element = EL_UNDEFINED;
3862 player->inventory_size = 0;
3864 if (level.use_initial_inventory[i])
3866 for (j = 0; j < level.initial_inventory_size[i]; j++)
3868 int element = level.initial_inventory_content[i][j];
3869 int collect_count = element_info[element].collect_count_initial;
3872 if (!IS_CUSTOM_ELEMENT(element))
3875 if (collect_count == 0)
3876 player->inventory_infinite_element = element;
3878 for (k = 0; k < collect_count; k++)
3879 if (player->inventory_size < MAX_INVENTORY_SIZE)
3880 player->inventory_element[player->inventory_size++] = element;
3884 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3885 SnapField(player, 0, 0);
3887 player->LevelSolved = FALSE;
3888 player->GameOver = FALSE;
3890 player->LevelSolved_GameWon = FALSE;
3891 player->LevelSolved_GameEnd = FALSE;
3892 player->LevelSolved_PanelOff = FALSE;
3893 player->LevelSolved_SaveTape = FALSE;
3894 player->LevelSolved_SaveScore = FALSE;
3895 player->LevelSolved_CountingTime = 0;
3896 player->LevelSolved_CountingScore = 0;
3898 map_player_action[i] = i;
3901 network_player_action_received = FALSE;
3903 #if defined(NETWORK_AVALIABLE)
3904 /* initial null action */
3905 if (network_playing)
3906 SendToServer_MovePlayer(MV_NONE);
3915 TimeLeft = level.time;
3918 ScreenMovDir = MV_NONE;
3922 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3924 AllPlayersGone = FALSE;
3926 game.yamyam_content_nr = 0;
3927 game.robot_wheel_active = FALSE;
3928 game.magic_wall_active = FALSE;
3929 game.magic_wall_time_left = 0;
3930 game.light_time_left = 0;
3931 game.timegate_time_left = 0;
3932 game.switchgate_pos = 0;
3933 game.wind_direction = level.wind_direction_initial;
3935 #if !USE_PLAYER_GRAVITY
3936 game.gravity = FALSE;
3937 game.explosions_delayed = TRUE;
3940 game.lenses_time_left = 0;
3941 game.magnify_time_left = 0;
3943 game.ball_state = level.ball_state_initial;
3944 game.ball_content_nr = 0;
3946 game.envelope_active = FALSE;
3948 /* set focus to local player for network games, else to all players */
3949 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3950 game.centered_player_nr_next = game.centered_player_nr;
3951 game.set_centered_player = FALSE;
3953 if (network_playing && tape.recording)
3955 /* store client dependent player focus when recording network games */
3956 tape.centered_player_nr_next = game.centered_player_nr_next;
3957 tape.set_centered_player = TRUE;
3960 for (i = 0; i < NUM_BELTS; i++)
3962 game.belt_dir[i] = MV_NONE;
3963 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3966 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3967 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3969 SCAN_PLAYFIELD(x, y)
3971 Feld[x][y] = level.field[x][y];
3972 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3973 ChangeDelay[x][y] = 0;
3974 ChangePage[x][y] = -1;
3975 #if USE_NEW_CUSTOM_VALUE
3976 CustomValue[x][y] = 0; /* initialized in InitField() */
3978 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3980 WasJustMoving[x][y] = 0;
3981 WasJustFalling[x][y] = 0;
3982 CheckCollision[x][y] = 0;
3983 CheckImpact[x][y] = 0;
3985 Pushed[x][y] = FALSE;
3987 ChangeCount[x][y] = 0;
3988 ChangeEvent[x][y] = -1;
3990 ExplodePhase[x][y] = 0;
3991 ExplodeDelay[x][y] = 0;
3992 ExplodeField[x][y] = EX_TYPE_NONE;
3994 RunnerVisit[x][y] = 0;
3995 PlayerVisit[x][y] = 0;
3998 GfxRandom[x][y] = INIT_GFX_RANDOM();
3999 GfxElement[x][y] = EL_UNDEFINED;
4000 GfxAction[x][y] = ACTION_DEFAULT;
4001 GfxDir[x][y] = MV_NONE;
4002 GfxRedraw[x][y] = GFX_REDRAW_NONE;
4005 SCAN_PLAYFIELD(x, y)
4007 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
4009 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
4011 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
4014 InitField(x, y, TRUE);
4016 ResetGfxAnimation(x, y);
4021 for (i = 0; i < MAX_PLAYERS; i++)
4023 struct PlayerInfo *player = &stored_player[i];
4025 /* set number of special actions for bored and sleeping animation */
4026 player->num_special_action_bored =
4027 get_num_special_action(player->artwork_element,
4028 ACTION_BORING_1, ACTION_BORING_LAST);
4029 player->num_special_action_sleeping =
4030 get_num_special_action(player->artwork_element,
4031 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
4034 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
4035 emulate_sb ? EMU_SOKOBAN :
4036 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
4038 #if USE_NEW_ALL_SLIPPERY
4039 /* initialize type of slippery elements */
4040 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4042 if (!IS_CUSTOM_ELEMENT(i))
4044 /* default: elements slip down either to the left or right randomly */
4045 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
4047 /* SP style elements prefer to slip down on the left side */
4048 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
4049 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4051 /* BD style elements prefer to slip down on the left side */
4052 if (game.emulation == EMU_BOULDERDASH)
4053 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4058 /* initialize explosion and ignition delay */
4059 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4061 if (!IS_CUSTOM_ELEMENT(i))
4064 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4065 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4066 game.emulation == EMU_SUPAPLEX ? 3 : 2);
4067 int last_phase = (num_phase + 1) * delay;
4068 int half_phase = (num_phase / 2) * delay;
4070 element_info[i].explosion_delay = last_phase - 1;
4071 element_info[i].ignition_delay = half_phase;
4073 if (i == EL_BLACK_ORB)
4074 element_info[i].ignition_delay = 1;
4078 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
4079 element_info[i].explosion_delay = 1;
4081 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
4082 element_info[i].ignition_delay = 1;
4086 /* correct non-moving belts to start moving left */
4087 for (i = 0; i < NUM_BELTS; i++)
4088 if (game.belt_dir[i] == MV_NONE)
4089 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
4091 #if USE_NEW_PLAYER_ASSIGNMENTS
4092 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4093 /* choose default local player */
4094 local_player = &stored_player[0];
4096 for (i = 0; i < MAX_PLAYERS; i++)
4097 stored_player[i].connected = FALSE;
4099 local_player->connected = TRUE;
4100 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4104 /* try to guess locally connected team mode players (needed for correct
4105 assignment of player figures from level to locally playing players) */
4107 for (i = 0; i < MAX_PLAYERS; i++)
4108 if (tape.player_participates[i])
4109 stored_player[i].connected = TRUE;
4111 else if (setup.team_mode && !options.network)
4113 /* try to guess locally connected team mode players (needed for correct
4114 assignment of player figures from level to locally playing players) */
4116 for (i = 0; i < MAX_PLAYERS; i++)
4117 if (setup.input[i].use_joystick ||
4118 setup.input[i].key.left != KSYM_UNDEFINED)
4119 stored_player[i].connected = TRUE;
4123 for (i = 0; i < MAX_PLAYERS; i++)
4124 printf("::: player %d: %s\n", i,
4125 (stored_player[i].connected ? "connected" : "not connected"));
4127 for (i = 0; i < MAX_PLAYERS; i++)
4128 printf("::: player %d: %s\n", i,
4129 (stored_player[i].present ? "present" : "not present"));
4132 /* check if any connected player was not found in playfield */
4133 for (i = 0; i < MAX_PLAYERS; i++)
4135 struct PlayerInfo *player = &stored_player[i];
4137 if (player->connected && !player->present)
4139 struct PlayerInfo *field_player = NULL;
4142 printf("::: looking for field player for player %d ...\n", i);
4145 /* assign first free player found that is present in the playfield */
4147 /* first try: look for unmapped playfield player that is not connected */
4148 if (field_player == NULL)
4149 for (j = 0; j < MAX_PLAYERS; j++)
4150 if (stored_player[j].present &&
4151 !stored_player[j].mapped &&
4152 !stored_player[j].connected)
4153 field_player = &stored_player[j];
4155 /* second try: look for *any* unmapped playfield player */
4156 if (field_player == NULL)
4157 for (j = 0; j < MAX_PLAYERS; j++)
4158 if (stored_player[j].present &&
4159 !stored_player[j].mapped)
4160 field_player = &stored_player[j];
4162 if (field_player != NULL)
4164 int jx = field_player->jx, jy = field_player->jy;
4167 printf("::: found player figure %d\n", field_player->index_nr);
4170 player->present = FALSE;
4171 player->active = FALSE;
4173 field_player->present = TRUE;
4174 field_player->active = TRUE;
4177 player->initial_element = field_player->initial_element;
4178 player->artwork_element = field_player->artwork_element;
4180 player->block_last_field = field_player->block_last_field;
4181 player->block_delay_adjustment = field_player->block_delay_adjustment;
4184 StorePlayer[jx][jy] = field_player->element_nr;
4186 field_player->jx = field_player->last_jx = jx;
4187 field_player->jy = field_player->last_jy = jy;
4189 if (local_player == player)
4190 local_player = field_player;
4192 map_player_action[field_player->index_nr] = i;
4194 field_player->mapped = TRUE;
4197 printf("::: map_player_action[%d] == %d\n",
4198 field_player->index_nr, i);
4203 if (player->connected && player->present)
4204 player->mapped = TRUE;
4209 /* check if any connected player was not found in playfield */
4210 for (i = 0; i < MAX_PLAYERS; i++)
4212 struct PlayerInfo *player = &stored_player[i];
4214 if (player->connected && !player->present)
4216 for (j = 0; j < MAX_PLAYERS; j++)
4218 struct PlayerInfo *field_player = &stored_player[j];
4219 int jx = field_player->jx, jy = field_player->jy;
4221 /* assign first free player found that is present in the playfield */
4222 if (field_player->present && !field_player->connected)
4224 player->present = TRUE;
4225 player->active = TRUE;
4227 field_player->present = FALSE;
4228 field_player->active = FALSE;
4230 player->initial_element = field_player->initial_element;
4231 player->artwork_element = field_player->artwork_element;
4233 player->block_last_field = field_player->block_last_field;
4234 player->block_delay_adjustment = field_player->block_delay_adjustment;
4236 StorePlayer[jx][jy] = player->element_nr;
4238 player->jx = player->last_jx = jx;
4239 player->jy = player->last_jy = jy;
4249 printf("::: local_player->present == %d\n", local_player->present);
4254 /* when playing a tape, eliminate all players who do not participate */
4256 #if USE_NEW_PLAYER_ASSIGNMENTS
4257 for (i = 0; i < MAX_PLAYERS; i++)
4259 if (stored_player[i].active &&
4260 !tape.player_participates[map_player_action[i]])
4262 struct PlayerInfo *player = &stored_player[i];
4263 int jx = player->jx, jy = player->jy;
4265 player->active = FALSE;
4266 StorePlayer[jx][jy] = 0;
4267 Feld[jx][jy] = EL_EMPTY;
4271 for (i = 0; i < MAX_PLAYERS; i++)
4273 if (stored_player[i].active &&
4274 !tape.player_participates[i])
4276 struct PlayerInfo *player = &stored_player[i];
4277 int jx = player->jx, jy = player->jy;
4279 player->active = FALSE;
4280 StorePlayer[jx][jy] = 0;
4281 Feld[jx][jy] = EL_EMPTY;
4286 else if (!options.network && !setup.team_mode) /* && !tape.playing */
4288 /* when in single player mode, eliminate all but the first active player */
4290 for (i = 0; i < MAX_PLAYERS; i++)
4292 if (stored_player[i].active)
4294 for (j = i + 1; j < MAX_PLAYERS; j++)
4296 if (stored_player[j].active)
4298 struct PlayerInfo *player = &stored_player[j];
4299 int jx = player->jx, jy = player->jy;
4301 player->active = FALSE;
4302 player->present = FALSE;
4304 StorePlayer[jx][jy] = 0;
4305 Feld[jx][jy] = EL_EMPTY;
4312 /* when recording the game, store which players take part in the game */
4315 #if USE_NEW_PLAYER_ASSIGNMENTS
4316 for (i = 0; i < MAX_PLAYERS; i++)
4317 if (stored_player[i].connected)
4318 tape.player_participates[i] = TRUE;
4320 for (i = 0; i < MAX_PLAYERS; i++)
4321 if (stored_player[i].active)
4322 tape.player_participates[i] = TRUE;
4328 for (i = 0; i < MAX_PLAYERS; i++)
4330 struct PlayerInfo *player = &stored_player[i];
4332 printf("Player %d: present == %d, connected == %d, active == %d.\n",
4337 if (local_player == player)
4338 printf("Player %d is local player.\n", i+1);
4342 if (BorderElement == EL_EMPTY)
4345 SBX_Right = lev_fieldx - SCR_FIELDX;
4347 SBY_Lower = lev_fieldy - SCR_FIELDY;
4352 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4354 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4357 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4358 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4360 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4361 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4363 /* if local player not found, look for custom element that might create
4364 the player (make some assumptions about the right custom element) */
4365 if (!local_player->present)
4367 int start_x = 0, start_y = 0;
4368 int found_rating = 0;
4369 int found_element = EL_UNDEFINED;
4370 int player_nr = local_player->index_nr;
4372 SCAN_PLAYFIELD(x, y)
4374 int element = Feld[x][y];
4379 if (level.use_start_element[player_nr] &&
4380 level.start_element[player_nr] == element &&
4387 found_element = element;
4390 if (!IS_CUSTOM_ELEMENT(element))
4393 if (CAN_CHANGE(element))
4395 for (i = 0; i < element_info[element].num_change_pages; i++)
4397 /* check for player created from custom element as single target */
4398 content = element_info[element].change_page[i].target_element;
4399 is_player = ELEM_IS_PLAYER(content);
4401 if (is_player && (found_rating < 3 ||
4402 (found_rating == 3 && element < found_element)))
4408 found_element = element;
4413 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4415 /* check for player created from custom element as explosion content */
4416 content = element_info[element].content.e[xx][yy];
4417 is_player = ELEM_IS_PLAYER(content);
4419 if (is_player && (found_rating < 2 ||
4420 (found_rating == 2 && element < found_element)))
4422 start_x = x + xx - 1;
4423 start_y = y + yy - 1;
4426 found_element = element;
4429 if (!CAN_CHANGE(element))
4432 for (i = 0; i < element_info[element].num_change_pages; i++)
4434 /* check for player created from custom element as extended target */
4436 element_info[element].change_page[i].target_content.e[xx][yy];
4438 is_player = ELEM_IS_PLAYER(content);
4440 if (is_player && (found_rating < 1 ||
4441 (found_rating == 1 && element < found_element)))
4443 start_x = x + xx - 1;
4444 start_y = y + yy - 1;
4447 found_element = element;
4453 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
4454 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4457 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4458 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4463 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
4464 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4465 local_player->jx - MIDPOSX);
4467 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4468 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4469 local_player->jy - MIDPOSY);
4473 /* do not use PLAYING mask for fading out from main screen */
4474 game_status = GAME_MODE_MAIN;
4479 if (!game.restart_level)
4480 CloseDoor(DOOR_CLOSE_1);
4483 if (level_editor_test_game)
4484 FadeSkipNextFadeIn();
4486 FadeSetEnterScreen();
4488 if (level_editor_test_game)
4489 fading = fading_none;
4491 fading = menu.destination;
4495 FadeOut(REDRAW_FIELD);
4498 FadeOut(REDRAW_FIELD);
4502 game_status = GAME_MODE_PLAYING;
4505 /* !!! FIX THIS (START) !!! */
4506 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4508 InitGameEngine_EM();
4510 /* blit playfield from scroll buffer to normal back buffer for fading in */
4511 BlitScreenToBitmap_EM(backbuffer);
4513 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4515 InitGameEngine_SP();
4517 /* blit playfield from scroll buffer to normal back buffer for fading in */
4518 BlitScreenToBitmap_SP(backbuffer);
4525 /* after drawing the level, correct some elements */
4526 if (game.timegate_time_left == 0)
4527 CloseAllOpenTimegates();
4529 /* blit playfield from scroll buffer to normal back buffer for fading in */
4530 if (setup.soft_scrolling)
4531 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4533 redraw_mask |= REDRAW_FROM_BACKBUFFER;
4535 /* !!! FIX THIS (END) !!! */
4538 FadeIn(REDRAW_FIELD);
4541 FadeIn(REDRAW_FIELD);
4546 if (!game.restart_level)
4548 /* copy default game door content to main double buffer */
4551 /* !!! CHECK AGAIN !!! */
4552 SetPanelBackground();
4553 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4554 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4556 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
4558 /* (ClearRectangle() only needed if panel bitmap is smaller than panel) */
4559 ClearRectangle(drawto, DX, DY, DXSIZE, DYSIZE);
4560 BlitBitmap(gfx->bitmap, drawto, gfx->src_x, gfx->src_y,
4561 MIN(gfx->width, DXSIZE), MIN(gfx->height, DYSIZE), DX, DY);
4564 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4565 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4569 SetPanelBackground();
4570 SetDrawBackgroundMask(REDRAW_DOOR_1);
4573 UpdateAndDisplayGameControlValues();
4575 UpdateGameDoorValues();
4576 DrawGameDoorValues();
4579 if (!game.restart_level)
4583 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4584 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4585 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4589 /* copy actual game door content to door double buffer for OpenDoor() */
4590 BlitBitmap(drawto, bitmap_db_door,
4591 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4593 OpenDoor(DOOR_OPEN_ALL);
4595 PlaySound(SND_GAME_STARTING);
4597 if (setup.sound_music)
4600 KeyboardAutoRepeatOffUnlessAutoplay();
4604 for (i = 0; i < MAX_PLAYERS; i++)
4605 printf("Player %d %sactive.\n",
4606 i + 1, (stored_player[i].active ? "" : "not "));
4617 game.restart_level = FALSE;
4620 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4622 /* this is used for non-R'n'D game engines to update certain engine values */
4624 /* needed to determine if sounds are played within the visible screen area */
4625 scroll_x = actual_scroll_x;
4626 scroll_y = actual_scroll_y;
4629 void InitMovDir(int x, int y)
4631 int i, element = Feld[x][y];
4632 static int xy[4][2] =
4639 static int direction[3][4] =
4641 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4642 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4643 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4652 Feld[x][y] = EL_BUG;
4653 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4656 case EL_SPACESHIP_RIGHT:
4657 case EL_SPACESHIP_UP:
4658 case EL_SPACESHIP_LEFT:
4659 case EL_SPACESHIP_DOWN:
4660 Feld[x][y] = EL_SPACESHIP;
4661 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4664 case EL_BD_BUTTERFLY_RIGHT:
4665 case EL_BD_BUTTERFLY_UP:
4666 case EL_BD_BUTTERFLY_LEFT:
4667 case EL_BD_BUTTERFLY_DOWN:
4668 Feld[x][y] = EL_BD_BUTTERFLY;
4669 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4672 case EL_BD_FIREFLY_RIGHT:
4673 case EL_BD_FIREFLY_UP:
4674 case EL_BD_FIREFLY_LEFT:
4675 case EL_BD_FIREFLY_DOWN:
4676 Feld[x][y] = EL_BD_FIREFLY;
4677 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4680 case EL_PACMAN_RIGHT:
4682 case EL_PACMAN_LEFT:
4683 case EL_PACMAN_DOWN:
4684 Feld[x][y] = EL_PACMAN;
4685 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4688 case EL_YAMYAM_LEFT:
4689 case EL_YAMYAM_RIGHT:
4691 case EL_YAMYAM_DOWN:
4692 Feld[x][y] = EL_YAMYAM;
4693 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4696 case EL_SP_SNIKSNAK:
4697 MovDir[x][y] = MV_UP;
4700 case EL_SP_ELECTRON:
4701 MovDir[x][y] = MV_LEFT;
4708 Feld[x][y] = EL_MOLE;
4709 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4713 if (IS_CUSTOM_ELEMENT(element))
4715 struct ElementInfo *ei = &element_info[element];
4716 int move_direction_initial = ei->move_direction_initial;
4717 int move_pattern = ei->move_pattern;
4719 if (move_direction_initial == MV_START_PREVIOUS)
4721 if (MovDir[x][y] != MV_NONE)
4724 move_direction_initial = MV_START_AUTOMATIC;
4727 if (move_direction_initial == MV_START_RANDOM)
4728 MovDir[x][y] = 1 << RND(4);
4729 else if (move_direction_initial & MV_ANY_DIRECTION)
4730 MovDir[x][y] = move_direction_initial;
4731 else if (move_pattern == MV_ALL_DIRECTIONS ||
4732 move_pattern == MV_TURNING_LEFT ||
4733 move_pattern == MV_TURNING_RIGHT ||
4734 move_pattern == MV_TURNING_LEFT_RIGHT ||
4735 move_pattern == MV_TURNING_RIGHT_LEFT ||
4736 move_pattern == MV_TURNING_RANDOM)
4737 MovDir[x][y] = 1 << RND(4);
4738 else if (move_pattern == MV_HORIZONTAL)
4739 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4740 else if (move_pattern == MV_VERTICAL)
4741 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4742 else if (move_pattern & MV_ANY_DIRECTION)
4743 MovDir[x][y] = element_info[element].move_pattern;
4744 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4745 move_pattern == MV_ALONG_RIGHT_SIDE)
4747 /* use random direction as default start direction */
4748 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4749 MovDir[x][y] = 1 << RND(4);
4751 for (i = 0; i < NUM_DIRECTIONS; i++)
4753 int x1 = x + xy[i][0];
4754 int y1 = y + xy[i][1];
4756 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4758 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4759 MovDir[x][y] = direction[0][i];
4761 MovDir[x][y] = direction[1][i];
4770 MovDir[x][y] = 1 << RND(4);
4772 if (element != EL_BUG &&
4773 element != EL_SPACESHIP &&
4774 element != EL_BD_BUTTERFLY &&
4775 element != EL_BD_FIREFLY)
4778 for (i = 0; i < NUM_DIRECTIONS; i++)
4780 int x1 = x + xy[i][0];
4781 int y1 = y + xy[i][1];
4783 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4785 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4787 MovDir[x][y] = direction[0][i];
4790 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4791 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4793 MovDir[x][y] = direction[1][i];
4802 GfxDir[x][y] = MovDir[x][y];
4805 void InitAmoebaNr(int x, int y)
4808 int group_nr = AmoebeNachbarNr(x, y);
4812 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4814 if (AmoebaCnt[i] == 0)
4822 AmoebaNr[x][y] = group_nr;
4823 AmoebaCnt[group_nr]++;
4824 AmoebaCnt2[group_nr]++;
4827 static void PlayerWins(struct PlayerInfo *player)
4829 player->LevelSolved = TRUE;
4830 player->GameOver = TRUE;
4832 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4833 level.native_em_level->lev->score : player->score);
4835 player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4836 player->LevelSolved_CountingScore = player->score_final;
4841 static int time, time_final;
4842 static int score, score_final;
4843 static int game_over_delay_1 = 0;
4844 static int game_over_delay_2 = 0;
4845 int game_over_delay_value_1 = 50;
4846 int game_over_delay_value_2 = 50;
4848 if (!local_player->LevelSolved_GameWon)
4852 /* do not start end game actions before the player stops moving (to exit) */
4853 if (local_player->MovPos)
4856 local_player->LevelSolved_GameWon = TRUE;
4857 local_player->LevelSolved_SaveTape = tape.recording;
4858 local_player->LevelSolved_SaveScore = !tape.playing;
4860 if (tape.auto_play) /* tape might already be stopped here */
4861 tape.auto_play_level_solved = TRUE;
4867 game_over_delay_1 = game_over_delay_value_1;
4868 game_over_delay_2 = game_over_delay_value_2;
4870 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4871 score = score_final = local_player->score_final;
4876 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4878 else if (level.time == 0 && TimePlayed < 999)
4881 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4884 local_player->score_final = score_final;
4886 if (level_editor_test_game)
4889 score = score_final;
4892 local_player->LevelSolved_CountingTime = time;
4893 local_player->LevelSolved_CountingScore = score;
4895 game_panel_controls[GAME_PANEL_TIME].value = time;
4896 game_panel_controls[GAME_PANEL_SCORE].value = score;
4898 DisplayGameControlValues();
4900 DrawGameValue_Time(time);
4901 DrawGameValue_Score(score);
4905 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4907 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4909 /* close exit door after last player */
4910 if ((AllPlayersGone &&
4911 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4912 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4913 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4914 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4915 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4917 int element = Feld[ExitX][ExitY];
4920 if (element == EL_EM_EXIT_OPEN ||
4921 element == EL_EM_STEEL_EXIT_OPEN)
4928 Feld[ExitX][ExitY] =
4929 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4930 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4931 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4932 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4933 EL_EM_STEEL_EXIT_CLOSING);
4935 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4939 /* player disappears */
4940 DrawLevelField(ExitX, ExitY);
4943 for (i = 0; i < MAX_PLAYERS; i++)
4945 struct PlayerInfo *player = &stored_player[i];
4947 if (player->present)
4949 RemovePlayer(player);
4951 /* player disappears */
4952 DrawLevelField(player->jx, player->jy);
4957 PlaySound(SND_GAME_WINNING);
4960 if (game_over_delay_1 > 0)
4962 game_over_delay_1--;
4967 if (time != time_final)
4969 int time_to_go = ABS(time_final - time);
4970 int time_count_dir = (time < time_final ? +1 : -1);
4971 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4973 time += time_count_steps * time_count_dir;
4974 score += time_count_steps * level.score[SC_TIME_BONUS];
4977 local_player->LevelSolved_CountingTime = time;
4978 local_player->LevelSolved_CountingScore = score;
4980 game_panel_controls[GAME_PANEL_TIME].value = time;
4981 game_panel_controls[GAME_PANEL_SCORE].value = score;
4983 DisplayGameControlValues();
4985 DrawGameValue_Time(time);
4986 DrawGameValue_Score(score);
4989 if (time == time_final)
4990 StopSound(SND_GAME_LEVELTIME_BONUS);
4991 else if (setup.sound_loops)
4992 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4994 PlaySound(SND_GAME_LEVELTIME_BONUS);
4999 local_player->LevelSolved_PanelOff = TRUE;
5001 if (game_over_delay_2 > 0)
5003 game_over_delay_2--;
5016 boolean raise_level = FALSE;
5018 local_player->LevelSolved_GameEnd = TRUE;
5020 CloseDoor(DOOR_CLOSE_1);
5022 if (local_player->LevelSolved_SaveTape)
5029 SaveTapeChecked(tape.level_nr); /* ask to save tape */
5031 SaveTape(tape.level_nr); /* ask to save tape */
5035 if (level_editor_test_game)
5037 game_status = GAME_MODE_MAIN;
5040 DrawAndFadeInMainMenu(REDRAW_FIELD);
5048 if (!local_player->LevelSolved_SaveScore)
5051 FadeOut(REDRAW_FIELD);
5054 game_status = GAME_MODE_MAIN;
5056 DrawAndFadeInMainMenu(REDRAW_FIELD);
5061 if (level_nr == leveldir_current->handicap_level)
5063 leveldir_current->handicap_level++;
5064 SaveLevelSetup_SeriesInfo();
5067 if (level_nr < leveldir_current->last_level)
5068 raise_level = TRUE; /* advance to next level */
5070 if ((hi_pos = NewHiScore()) >= 0)
5072 game_status = GAME_MODE_SCORES;
5074 DrawHallOfFame(hi_pos);
5085 FadeOut(REDRAW_FIELD);
5088 game_status = GAME_MODE_MAIN;
5096 DrawAndFadeInMainMenu(REDRAW_FIELD);
5105 LoadScore(level_nr);
5107 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5108 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
5111 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
5113 if (local_player->score_final > highscore[k].Score)
5115 /* player has made it to the hall of fame */
5117 if (k < MAX_SCORE_ENTRIES - 1)
5119 int m = MAX_SCORE_ENTRIES - 1;
5122 for (l = k; l < MAX_SCORE_ENTRIES; l++)
5123 if (strEqual(setup.player_name, highscore[l].Name))
5125 if (m == k) /* player's new highscore overwrites his old one */
5129 for (l = m; l > k; l--)
5131 strcpy(highscore[l].Name, highscore[l - 1].Name);
5132 highscore[l].Score = highscore[l - 1].Score;
5139 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5140 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5141 highscore[k].Score = local_player->score_final;
5147 else if (!strncmp(setup.player_name, highscore[k].Name,
5148 MAX_PLAYER_NAME_LEN))
5149 break; /* player already there with a higher score */
5155 SaveScore(level_nr);
5160 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
5162 int element = Feld[x][y];
5163 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5164 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5165 int horiz_move = (dx != 0);
5166 int sign = (horiz_move ? dx : dy);
5167 int step = sign * element_info[element].move_stepsize;
5169 /* special values for move stepsize for spring and things on conveyor belt */
5172 if (CAN_FALL(element) &&
5173 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5174 step = sign * MOVE_STEPSIZE_NORMAL / 2;
5175 else if (element == EL_SPRING)
5176 step = sign * MOVE_STEPSIZE_NORMAL * 2;
5182 inline static int getElementMoveStepsize(int x, int y)
5184 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5187 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5189 if (player->GfxAction != action || player->GfxDir != dir)
5192 printf("Player frame reset! (%d => %d, %d => %d)\n",
5193 player->GfxAction, action, player->GfxDir, dir);
5196 player->GfxAction = action;
5197 player->GfxDir = dir;
5199 player->StepFrame = 0;
5203 #if USE_GFX_RESET_GFX_ANIMATION
5204 static void ResetGfxFrame(int x, int y, boolean redraw)
5206 int element = Feld[x][y];
5207 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5208 int last_gfx_frame = GfxFrame[x][y];
5210 if (graphic_info[graphic].anim_global_sync)
5211 GfxFrame[x][y] = FrameCounter;
5212 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5213 GfxFrame[x][y] = CustomValue[x][y];
5214 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5215 GfxFrame[x][y] = element_info[element].collect_score;
5216 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5217 GfxFrame[x][y] = ChangeDelay[x][y];
5219 if (redraw && GfxFrame[x][y] != last_gfx_frame)
5220 DrawLevelGraphicAnimation(x, y, graphic);
5224 static void ResetGfxAnimation(int x, int y)
5226 GfxAction[x][y] = ACTION_DEFAULT;
5227 GfxDir[x][y] = MovDir[x][y];
5230 #if USE_GFX_RESET_GFX_ANIMATION
5231 ResetGfxFrame(x, y, FALSE);
5235 static void ResetRandomAnimationValue(int x, int y)
5237 GfxRandom[x][y] = INIT_GFX_RANDOM();
5240 void InitMovingField(int x, int y, int direction)
5242 int element = Feld[x][y];
5243 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5244 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5247 boolean is_moving_before, is_moving_after;
5249 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5252 /* check if element was/is moving or being moved before/after mode change */
5255 is_moving_before = (WasJustMoving[x][y] != 0);
5257 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5258 is_moving_before = WasJustMoving[x][y];
5261 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5263 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
5265 /* reset animation only for moving elements which change direction of moving
5266 or which just started or stopped moving
5267 (else CEs with property "can move" / "not moving" are reset each frame) */
5268 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5270 if (is_moving_before != is_moving_after ||
5271 direction != MovDir[x][y])
5272 ResetGfxAnimation(x, y);
5274 if ((is_moving_before || is_moving_after) && !continues_moving)
5275 ResetGfxAnimation(x, y);
5278 if (!continues_moving)
5279 ResetGfxAnimation(x, y);
5282 MovDir[x][y] = direction;
5283 GfxDir[x][y] = direction;
5285 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5286 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5287 direction == MV_DOWN && CAN_FALL(element) ?
5288 ACTION_FALLING : ACTION_MOVING);
5290 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5291 ACTION_FALLING : ACTION_MOVING);
5294 /* this is needed for CEs with property "can move" / "not moving" */
5296 if (is_moving_after)
5298 if (Feld[newx][newy] == EL_EMPTY)
5299 Feld[newx][newy] = EL_BLOCKED;
5301 MovDir[newx][newy] = MovDir[x][y];
5303 #if USE_NEW_CUSTOM_VALUE
5304 CustomValue[newx][newy] = CustomValue[x][y];
5307 GfxFrame[newx][newy] = GfxFrame[x][y];
5308 GfxRandom[newx][newy] = GfxRandom[x][y];
5309 GfxAction[newx][newy] = GfxAction[x][y];
5310 GfxDir[newx][newy] = GfxDir[x][y];
5314 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5316 int direction = MovDir[x][y];
5317 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5318 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
5324 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5326 int oldx = x, oldy = y;
5327 int direction = MovDir[x][y];
5329 if (direction == MV_LEFT)
5331 else if (direction == MV_RIGHT)
5333 else if (direction == MV_UP)
5335 else if (direction == MV_DOWN)
5338 *comes_from_x = oldx;
5339 *comes_from_y = oldy;
5342 int MovingOrBlocked2Element(int x, int y)
5344 int element = Feld[x][y];
5346 if (element == EL_BLOCKED)
5350 Blocked2Moving(x, y, &oldx, &oldy);
5351 return Feld[oldx][oldy];
5357 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5359 /* like MovingOrBlocked2Element(), but if element is moving
5360 and (x,y) is the field the moving element is just leaving,
5361 return EL_BLOCKED instead of the element value */
5362 int element = Feld[x][y];
5364 if (IS_MOVING(x, y))
5366 if (element == EL_BLOCKED)
5370 Blocked2Moving(x, y, &oldx, &oldy);
5371 return Feld[oldx][oldy];
5380 static void RemoveField(int x, int y)
5382 Feld[x][y] = EL_EMPTY;
5388 #if USE_NEW_CUSTOM_VALUE
5389 CustomValue[x][y] = 0;
5393 ChangeDelay[x][y] = 0;
5394 ChangePage[x][y] = -1;
5395 Pushed[x][y] = FALSE;
5398 ExplodeField[x][y] = EX_TYPE_NONE;
5401 GfxElement[x][y] = EL_UNDEFINED;
5402 GfxAction[x][y] = ACTION_DEFAULT;
5403 GfxDir[x][y] = MV_NONE;
5405 /* !!! this would prevent the removed tile from being redrawn !!! */
5406 GfxRedraw[x][y] = GFX_REDRAW_NONE;
5410 void RemoveMovingField(int x, int y)
5412 int oldx = x, oldy = y, newx = x, newy = y;
5413 int element = Feld[x][y];
5414 int next_element = EL_UNDEFINED;
5416 if (element != EL_BLOCKED && !IS_MOVING(x, y))
5419 if (IS_MOVING(x, y))
5421 Moving2Blocked(x, y, &newx, &newy);
5423 if (Feld[newx][newy] != EL_BLOCKED)
5425 /* element is moving, but target field is not free (blocked), but
5426 already occupied by something different (example: acid pool);
5427 in this case, only remove the moving field, but not the target */
5429 RemoveField(oldx, oldy);
5431 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5433 TEST_DrawLevelField(oldx, oldy);
5438 else if (element == EL_BLOCKED)
5440 Blocked2Moving(x, y, &oldx, &oldy);
5441 if (!IS_MOVING(oldx, oldy))
5445 if (element == EL_BLOCKED &&
5446 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5447 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5448 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5449 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5450 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5451 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5452 next_element = get_next_element(Feld[oldx][oldy]);
5454 RemoveField(oldx, oldy);
5455 RemoveField(newx, newy);
5457 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5459 if (next_element != EL_UNDEFINED)
5460 Feld[oldx][oldy] = next_element;
5462 TEST_DrawLevelField(oldx, oldy);
5463 TEST_DrawLevelField(newx, newy);
5466 void DrawDynamite(int x, int y)
5468 int sx = SCREENX(x), sy = SCREENY(y);
5469 int graphic = el2img(Feld[x][y]);
5472 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5475 if (IS_WALKABLE_INSIDE(Back[x][y]))
5479 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5480 else if (Store[x][y])
5481 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5483 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5485 if (Back[x][y] || Store[x][y])
5486 DrawGraphicThruMask(sx, sy, graphic, frame);
5488 DrawGraphic(sx, sy, graphic, frame);
5491 void CheckDynamite(int x, int y)
5493 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5497 if (MovDelay[x][y] != 0)
5500 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5506 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5511 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5513 boolean num_checked_players = 0;
5516 for (i = 0; i < MAX_PLAYERS; i++)
5518 if (stored_player[i].active)
5520 int sx = stored_player[i].jx;
5521 int sy = stored_player[i].jy;
5523 if (num_checked_players == 0)
5530 *sx1 = MIN(*sx1, sx);
5531 *sy1 = MIN(*sy1, sy);
5532 *sx2 = MAX(*sx2, sx);
5533 *sy2 = MAX(*sy2, sy);
5536 num_checked_players++;
5541 static boolean checkIfAllPlayersFitToScreen_RND()
5543 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5545 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5547 return (sx2 - sx1 < SCR_FIELDX &&
5548 sy2 - sy1 < SCR_FIELDY);
5551 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5553 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5555 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5557 *sx = (sx1 + sx2) / 2;
5558 *sy = (sy1 + sy2) / 2;
5561 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5562 boolean center_screen, boolean quick_relocation)
5564 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5565 boolean no_delay = (tape.warp_forward);
5566 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5567 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5569 if (quick_relocation)
5571 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5573 if (!level.shifted_relocation || center_screen)
5575 /* quick relocation (without scrolling), with centering of screen */
5577 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5578 x > SBX_Right + MIDPOSX ? SBX_Right :
5581 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5582 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5587 /* quick relocation (without scrolling), but do not center screen */
5589 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5590 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5593 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5594 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5597 int offset_x = x + (scroll_x - center_scroll_x);
5598 int offset_y = y + (scroll_y - center_scroll_y);
5600 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5601 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5602 offset_x - MIDPOSX);
5604 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5605 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5606 offset_y - MIDPOSY);
5612 if (!level.shifted_relocation || center_screen)
5614 /* quick relocation (without scrolling), with centering of screen */
5616 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5617 x > SBX_Right + MIDPOSX ? SBX_Right :
5620 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5621 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5626 /* quick relocation (without scrolling), but do not center screen */
5628 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5629 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5632 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5633 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5636 int offset_x = x + (scroll_x - center_scroll_x);
5637 int offset_y = y + (scroll_y - center_scroll_y);
5639 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5640 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5641 offset_x - MIDPOSX);
5643 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5644 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5645 offset_y - MIDPOSY);
5648 /* quick relocation (without scrolling), inside visible screen area */
5650 int offset = game.scroll_delay_value;
5652 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
5653 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5654 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5656 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
5657 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5658 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5660 /* don't scroll over playfield boundaries */
5661 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5662 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5664 /* don't scroll over playfield boundaries */
5665 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5666 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5670 RedrawPlayfield(TRUE, 0,0,0,0);
5675 int scroll_xx, scroll_yy;
5677 if (!level.shifted_relocation || center_screen)
5679 /* visible relocation (with scrolling), with centering of screen */
5681 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5682 x > SBX_Right + MIDPOSX ? SBX_Right :
5685 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5686 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5691 /* visible relocation (with scrolling), but do not center screen */
5693 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5694 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5697 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5698 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5701 int offset_x = x + (scroll_x - center_scroll_x);
5702 int offset_y = y + (scroll_y - center_scroll_y);
5704 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5705 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5706 offset_x - MIDPOSX);
5708 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5709 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5710 offset_y - MIDPOSY);
5715 /* visible relocation (with scrolling), with centering of screen */
5717 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5718 x > SBX_Right + MIDPOSX ? SBX_Right :
5721 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5722 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5726 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5728 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5731 int fx = FX, fy = FY;
5733 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5734 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5736 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5742 fx += dx * TILEX / 2;
5743 fy += dy * TILEY / 2;
5745 ScrollLevel(dx, dy);
5748 /* scroll in two steps of half tile size to make things smoother */
5749 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5751 Delay(wait_delay_value);
5753 /* scroll second step to align at full tile size */
5755 Delay(wait_delay_value);
5760 Delay(wait_delay_value);
5764 void RelocatePlayer(int jx, int jy, int el_player_raw)
5766 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5767 int player_nr = GET_PLAYER_NR(el_player);
5768 struct PlayerInfo *player = &stored_player[player_nr];
5769 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5770 boolean no_delay = (tape.warp_forward);
5771 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5772 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5773 int old_jx = player->jx;
5774 int old_jy = player->jy;
5775 int old_element = Feld[old_jx][old_jy];
5776 int element = Feld[jx][jy];
5777 boolean player_relocated = (old_jx != jx || old_jy != jy);
5779 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5780 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5781 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5782 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5783 int leave_side_horiz = move_dir_horiz;
5784 int leave_side_vert = move_dir_vert;
5785 int enter_side = enter_side_horiz | enter_side_vert;
5786 int leave_side = leave_side_horiz | leave_side_vert;
5788 if (player->GameOver) /* do not reanimate dead player */
5791 if (!player_relocated) /* no need to relocate the player */
5794 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5796 RemoveField(jx, jy); /* temporarily remove newly placed player */
5797 DrawLevelField(jx, jy);
5800 if (player->present)
5802 while (player->MovPos)
5804 ScrollPlayer(player, SCROLL_GO_ON);
5805 ScrollScreen(NULL, SCROLL_GO_ON);
5807 AdvanceFrameAndPlayerCounters(player->index_nr);
5812 Delay(wait_delay_value);
5815 DrawPlayer(player); /* needed here only to cleanup last field */
5816 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5818 player->is_moving = FALSE;
5821 if (IS_CUSTOM_ELEMENT(old_element))
5822 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5824 player->index_bit, leave_side);
5826 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5828 player->index_bit, leave_side);
5830 Feld[jx][jy] = el_player;
5831 InitPlayerField(jx, jy, el_player, TRUE);
5833 /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5834 possible that the relocation target field did not contain a player element,
5835 but a walkable element, to which the new player was relocated -- in this
5836 case, restore that (already initialized!) element on the player field */
5837 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5839 Feld[jx][jy] = element; /* restore previously existing element */
5841 /* !!! do not initialize already initialized element a second time !!! */
5842 /* (this causes at least problems with "element creation" CE trigger for
5843 already existing elements, and existing Sokoban fields counted twice) */
5844 InitField(jx, jy, FALSE);
5848 /* only visually relocate centered player */
5849 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5850 FALSE, level.instant_relocation);
5852 TestIfPlayerTouchesBadThing(jx, jy);
5853 TestIfPlayerTouchesCustomElement(jx, jy);
5855 if (IS_CUSTOM_ELEMENT(element))
5856 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5857 player->index_bit, enter_side);
5859 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5860 player->index_bit, enter_side);
5863 if (player->is_switching)
5865 /* ensure that relocation while still switching an element does not cause
5866 a new element to be treated as also switched directly after relocation
5867 (this is important for teleporter switches that teleport the player to
5868 a place where another teleporter switch is in the same direction, which
5869 would then incorrectly be treated as immediately switched before the
5870 direction key that caused the switch was released) */
5872 player->switch_x += jx - old_jx;
5873 player->switch_y += jy - old_jy;
5878 void Explode(int ex, int ey, int phase, int mode)
5884 /* !!! eliminate this variable !!! */
5885 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5887 if (game.explosions_delayed)
5889 ExplodeField[ex][ey] = mode;
5893 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5895 int center_element = Feld[ex][ey];
5896 int artwork_element, explosion_element; /* set these values later */
5899 /* --- This is only really needed (and now handled) in "Impact()". --- */
5900 /* do not explode moving elements that left the explode field in time */
5901 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5902 center_element == EL_EMPTY &&
5903 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5908 /* !!! at this place, the center element may be EL_BLOCKED !!! */
5909 if (mode == EX_TYPE_NORMAL ||
5910 mode == EX_TYPE_CENTER ||
5911 mode == EX_TYPE_CROSS)
5912 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5915 /* remove things displayed in background while burning dynamite */
5916 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5919 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5921 /* put moving element to center field (and let it explode there) */
5922 center_element = MovingOrBlocked2Element(ex, ey);
5923 RemoveMovingField(ex, ey);
5924 Feld[ex][ey] = center_element;
5927 /* now "center_element" is finally determined -- set related values now */
5928 artwork_element = center_element; /* for custom player artwork */
5929 explosion_element = center_element; /* for custom player artwork */
5931 if (IS_PLAYER(ex, ey))
5933 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5935 artwork_element = stored_player[player_nr].artwork_element;
5937 if (level.use_explosion_element[player_nr])
5939 explosion_element = level.explosion_element[player_nr];
5940 artwork_element = explosion_element;
5945 if (mode == EX_TYPE_NORMAL ||
5946 mode == EX_TYPE_CENTER ||
5947 mode == EX_TYPE_CROSS)
5948 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5951 last_phase = element_info[explosion_element].explosion_delay + 1;
5953 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5955 int xx = x - ex + 1;
5956 int yy = y - ey + 1;
5959 if (!IN_LEV_FIELD(x, y) ||
5960 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5961 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5964 element = Feld[x][y];
5966 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5968 element = MovingOrBlocked2Element(x, y);
5970 if (!IS_EXPLOSION_PROOF(element))
5971 RemoveMovingField(x, y);
5974 /* indestructible elements can only explode in center (but not flames) */
5975 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5976 mode == EX_TYPE_BORDER)) ||
5977 element == EL_FLAMES)
5980 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5981 behaviour, for example when touching a yamyam that explodes to rocks
5982 with active deadly shield, a rock is created under the player !!! */
5983 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5985 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5986 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5987 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5989 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5992 if (IS_ACTIVE_BOMB(element))
5994 /* re-activate things under the bomb like gate or penguin */
5995 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
6002 /* save walkable background elements while explosion on same tile */
6003 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
6004 (x != ex || y != ey || mode == EX_TYPE_BORDER))
6005 Back[x][y] = element;
6007 /* ignite explodable elements reached by other explosion */
6008 if (element == EL_EXPLOSION)
6009 element = Store2[x][y];
6011 if (AmoebaNr[x][y] &&
6012 (element == EL_AMOEBA_FULL ||
6013 element == EL_BD_AMOEBA ||
6014 element == EL_AMOEBA_GROWING))
6016 AmoebaCnt[AmoebaNr[x][y]]--;
6017 AmoebaCnt2[AmoebaNr[x][y]]--;
6022 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
6024 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
6026 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
6028 if (PLAYERINFO(ex, ey)->use_murphy)
6029 Store[x][y] = EL_EMPTY;
6032 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
6033 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
6034 else if (ELEM_IS_PLAYER(center_element))
6035 Store[x][y] = EL_EMPTY;
6036 else if (center_element == EL_YAMYAM)
6037 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
6038 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
6039 Store[x][y] = element_info[center_element].content.e[xx][yy];
6041 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6042 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6043 otherwise) -- FIX THIS !!! */
6044 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6045 Store[x][y] = element_info[element].content.e[1][1];
6047 else if (!CAN_EXPLODE(element))
6048 Store[x][y] = element_info[element].content.e[1][1];
6051 Store[x][y] = EL_EMPTY;
6053 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6054 center_element == EL_AMOEBA_TO_DIAMOND)
6055 Store2[x][y] = element;
6057 Feld[x][y] = EL_EXPLOSION;
6058 GfxElement[x][y] = artwork_element;
6060 ExplodePhase[x][y] = 1;
6061 ExplodeDelay[x][y] = last_phase;
6066 if (center_element == EL_YAMYAM)
6067 game.yamyam_content_nr =
6068 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6080 GfxFrame[x][y] = 0; /* restart explosion animation */
6082 last_phase = ExplodeDelay[x][y];
6084 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6088 /* activate this even in non-DEBUG version until cause for crash in
6089 getGraphicAnimationFrame() (see below) is found and eliminated */
6095 /* this can happen if the player leaves an explosion just in time */
6096 if (GfxElement[x][y] == EL_UNDEFINED)
6097 GfxElement[x][y] = EL_EMPTY;
6099 if (GfxElement[x][y] == EL_UNDEFINED)
6102 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
6103 printf("Explode(): This should never happen!\n");
6106 GfxElement[x][y] = EL_EMPTY;
6112 border_element = Store2[x][y];
6113 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6114 border_element = StorePlayer[x][y];
6116 if (phase == element_info[border_element].ignition_delay ||
6117 phase == last_phase)
6119 boolean border_explosion = FALSE;
6121 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6122 !PLAYER_EXPLOSION_PROTECTED(x, y))
6124 KillPlayerUnlessExplosionProtected(x, y);
6125 border_explosion = TRUE;
6127 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6129 Feld[x][y] = Store2[x][y];
6132 border_explosion = TRUE;
6134 else if (border_element == EL_AMOEBA_TO_DIAMOND)
6136 AmoebeUmwandeln(x, y);
6138 border_explosion = TRUE;
6141 /* if an element just explodes due to another explosion (chain-reaction),
6142 do not immediately end the new explosion when it was the last frame of
6143 the explosion (as it would be done in the following "if"-statement!) */
6144 if (border_explosion && phase == last_phase)
6148 if (phase == last_phase)
6152 element = Feld[x][y] = Store[x][y];
6153 Store[x][y] = Store2[x][y] = 0;
6154 GfxElement[x][y] = EL_UNDEFINED;
6156 /* player can escape from explosions and might therefore be still alive */
6157 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6158 element <= EL_PLAYER_IS_EXPLODING_4)
6160 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6161 int explosion_element = EL_PLAYER_1 + player_nr;
6162 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6163 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6165 if (level.use_explosion_element[player_nr])
6166 explosion_element = level.explosion_element[player_nr];
6168 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6169 element_info[explosion_element].content.e[xx][yy]);
6172 /* restore probably existing indestructible background element */
6173 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6174 element = Feld[x][y] = Back[x][y];
6177 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6178 GfxDir[x][y] = MV_NONE;
6179 ChangeDelay[x][y] = 0;
6180 ChangePage[x][y] = -1;
6182 #if USE_NEW_CUSTOM_VALUE
6183 CustomValue[x][y] = 0;
6186 InitField_WithBug2(x, y, FALSE);
6188 TEST_DrawLevelField(x, y);
6190 TestIfElementTouchesCustomElement(x, y);
6192 if (GFX_CRUMBLED(element))
6193 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6195 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6196 StorePlayer[x][y] = 0;
6198 if (ELEM_IS_PLAYER(element))
6199 RelocatePlayer(x, y, element);
6201 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6203 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6204 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6207 TEST_DrawLevelFieldCrumbled(x, y);
6209 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6211 DrawLevelElement(x, y, Back[x][y]);
6212 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6214 else if (IS_WALKABLE_UNDER(Back[x][y]))
6216 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6217 DrawLevelElementThruMask(x, y, Back[x][y]);
6219 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6220 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6224 void DynaExplode(int ex, int ey)
6227 int dynabomb_element = Feld[ex][ey];
6228 int dynabomb_size = 1;
6229 boolean dynabomb_xl = FALSE;
6230 struct PlayerInfo *player;
6231 static int xy[4][2] =
6239 if (IS_ACTIVE_BOMB(dynabomb_element))
6241 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6242 dynabomb_size = player->dynabomb_size;
6243 dynabomb_xl = player->dynabomb_xl;
6244 player->dynabombs_left++;
6247 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6249 for (i = 0; i < NUM_DIRECTIONS; i++)
6251 for (j = 1; j <= dynabomb_size; j++)
6253 int x = ex + j * xy[i][0];
6254 int y = ey + j * xy[i][1];
6257 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6260 element = Feld[x][y];
6262 /* do not restart explosions of fields with active bombs */
6263 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6266 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6268 if (element != EL_EMPTY && element != EL_EXPLOSION &&
6269 !IS_DIGGABLE(element) && !dynabomb_xl)
6275 void Bang(int x, int y)
6277 int element = MovingOrBlocked2Element(x, y);
6278 int explosion_type = EX_TYPE_NORMAL;
6280 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6282 struct PlayerInfo *player = PLAYERINFO(x, y);
6284 #if USE_FIX_CE_ACTION_WITH_PLAYER
6285 element = Feld[x][y] = player->initial_element;
6287 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6288 player->element_nr);
6291 if (level.use_explosion_element[player->index_nr])
6293 int explosion_element = level.explosion_element[player->index_nr];
6295 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6296 explosion_type = EX_TYPE_CROSS;
6297 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6298 explosion_type = EX_TYPE_CENTER;
6306 case EL_BD_BUTTERFLY:
6309 case EL_DARK_YAMYAM:
6313 RaiseScoreElement(element);
6316 case EL_DYNABOMB_PLAYER_1_ACTIVE:
6317 case EL_DYNABOMB_PLAYER_2_ACTIVE:
6318 case EL_DYNABOMB_PLAYER_3_ACTIVE:
6319 case EL_DYNABOMB_PLAYER_4_ACTIVE:
6320 case EL_DYNABOMB_INCREASE_NUMBER:
6321 case EL_DYNABOMB_INCREASE_SIZE:
6322 case EL_DYNABOMB_INCREASE_POWER:
6323 explosion_type = EX_TYPE_DYNA;
6326 case EL_DC_LANDMINE:
6328 case EL_EM_EXIT_OPEN:
6329 case EL_EM_STEEL_EXIT_OPEN:
6331 explosion_type = EX_TYPE_CENTER;
6336 case EL_LAMP_ACTIVE:
6337 case EL_AMOEBA_TO_DIAMOND:
6338 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
6339 explosion_type = EX_TYPE_CENTER;
6343 if (element_info[element].explosion_type == EXPLODES_CROSS)
6344 explosion_type = EX_TYPE_CROSS;
6345 else if (element_info[element].explosion_type == EXPLODES_1X1)
6346 explosion_type = EX_TYPE_CENTER;
6350 if (explosion_type == EX_TYPE_DYNA)
6353 Explode(x, y, EX_PHASE_START, explosion_type);
6355 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6358 void SplashAcid(int x, int y)
6360 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6361 (!IN_LEV_FIELD(x - 1, y - 2) ||
6362 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6363 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6365 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6366 (!IN_LEV_FIELD(x + 1, y - 2) ||
6367 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6368 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6370 PlayLevelSound(x, y, SND_ACID_SPLASHING);
6373 static void InitBeltMovement()
6375 static int belt_base_element[4] =
6377 EL_CONVEYOR_BELT_1_LEFT,
6378 EL_CONVEYOR_BELT_2_LEFT,
6379 EL_CONVEYOR_BELT_3_LEFT,
6380 EL_CONVEYOR_BELT_4_LEFT
6382 static int belt_base_active_element[4] =
6384 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6385 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6386 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6387 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6392 /* set frame order for belt animation graphic according to belt direction */
6393 for (i = 0; i < NUM_BELTS; i++)
6397 for (j = 0; j < NUM_BELT_PARTS; j++)
6399 int element = belt_base_active_element[belt_nr] + j;
6400 int graphic_1 = el2img(element);
6401 int graphic_2 = el2panelimg(element);
6403 if (game.belt_dir[i] == MV_LEFT)
6405 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6406 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6410 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6411 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6416 SCAN_PLAYFIELD(x, y)
6418 int element = Feld[x][y];
6420 for (i = 0; i < NUM_BELTS; i++)
6422 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6424 int e_belt_nr = getBeltNrFromBeltElement(element);
6427 if (e_belt_nr == belt_nr)
6429 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6431 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6438 static void ToggleBeltSwitch(int x, int y)
6440 static int belt_base_element[4] =
6442 EL_CONVEYOR_BELT_1_LEFT,
6443 EL_CONVEYOR_BELT_2_LEFT,
6444 EL_CONVEYOR_BELT_3_LEFT,
6445 EL_CONVEYOR_BELT_4_LEFT
6447 static int belt_base_active_element[4] =
6449 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6450 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6451 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6452 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6454 static int belt_base_switch_element[4] =
6456 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6457 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6458 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6459 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6461 static int belt_move_dir[4] =
6469 int element = Feld[x][y];
6470 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6471 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6472 int belt_dir = belt_move_dir[belt_dir_nr];
6475 if (!IS_BELT_SWITCH(element))
6478 game.belt_dir_nr[belt_nr] = belt_dir_nr;
6479 game.belt_dir[belt_nr] = belt_dir;
6481 if (belt_dir_nr == 3)
6484 /* set frame order for belt animation graphic according to belt direction */
6485 for (i = 0; i < NUM_BELT_PARTS; i++)
6487 int element = belt_base_active_element[belt_nr] + i;
6488 int graphic_1 = el2img(element);
6489 int graphic_2 = el2panelimg(element);
6491 if (belt_dir == MV_LEFT)
6493 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6494 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6498 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6499 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6503 SCAN_PLAYFIELD(xx, yy)
6505 int element = Feld[xx][yy];
6507 if (IS_BELT_SWITCH(element))
6509 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6511 if (e_belt_nr == belt_nr)
6513 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6514 TEST_DrawLevelField(xx, yy);
6517 else if (IS_BELT(element) && belt_dir != MV_NONE)
6519 int e_belt_nr = getBeltNrFromBeltElement(element);
6521 if (e_belt_nr == belt_nr)
6523 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6525 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6526 TEST_DrawLevelField(xx, yy);
6529 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6531 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6533 if (e_belt_nr == belt_nr)
6535 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6537 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6538 TEST_DrawLevelField(xx, yy);
6544 static void ToggleSwitchgateSwitch(int x, int y)
6548 game.switchgate_pos = !game.switchgate_pos;
6550 SCAN_PLAYFIELD(xx, yy)
6552 int element = Feld[xx][yy];
6554 #if !USE_BOTH_SWITCHGATE_SWITCHES
6555 if (element == EL_SWITCHGATE_SWITCH_UP ||
6556 element == EL_SWITCHGATE_SWITCH_DOWN)
6558 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6559 TEST_DrawLevelField(xx, yy);
6561 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6562 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6564 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6565 TEST_DrawLevelField(xx, yy);
6568 if (element == EL_SWITCHGATE_SWITCH_UP)
6570 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6571 TEST_DrawLevelField(xx, yy);
6573 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6575 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6576 TEST_DrawLevelField(xx, yy);
6578 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6580 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6581 TEST_DrawLevelField(xx, yy);
6583 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6585 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6586 TEST_DrawLevelField(xx, yy);
6589 else if (element == EL_SWITCHGATE_OPEN ||
6590 element == EL_SWITCHGATE_OPENING)
6592 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6594 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6596 else if (element == EL_SWITCHGATE_CLOSED ||
6597 element == EL_SWITCHGATE_CLOSING)
6599 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6601 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6606 static int getInvisibleActiveFromInvisibleElement(int element)
6608 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6609 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6610 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6614 static int getInvisibleFromInvisibleActiveElement(int element)
6616 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6617 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6618 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6622 static void RedrawAllLightSwitchesAndInvisibleElements()
6626 SCAN_PLAYFIELD(x, y)
6628 int element = Feld[x][y];
6630 if (element == EL_LIGHT_SWITCH &&
6631 game.light_time_left > 0)
6633 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6634 TEST_DrawLevelField(x, y);
6636 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6637 game.light_time_left == 0)
6639 Feld[x][y] = EL_LIGHT_SWITCH;
6640 TEST_DrawLevelField(x, y);
6642 else if (element == EL_EMC_DRIPPER &&
6643 game.light_time_left > 0)
6645 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6646 TEST_DrawLevelField(x, y);
6648 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6649 game.light_time_left == 0)
6651 Feld[x][y] = EL_EMC_DRIPPER;
6652 TEST_DrawLevelField(x, y);
6654 else if (element == EL_INVISIBLE_STEELWALL ||
6655 element == EL_INVISIBLE_WALL ||
6656 element == EL_INVISIBLE_SAND)
6658 if (game.light_time_left > 0)
6659 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6661 TEST_DrawLevelField(x, y);
6663 /* uncrumble neighbour fields, if needed */
6664 if (element == EL_INVISIBLE_SAND)
6665 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6667 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6668 element == EL_INVISIBLE_WALL_ACTIVE ||
6669 element == EL_INVISIBLE_SAND_ACTIVE)
6671 if (game.light_time_left == 0)
6672 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6674 TEST_DrawLevelField(x, y);
6676 /* re-crumble neighbour fields, if needed */
6677 if (element == EL_INVISIBLE_SAND)
6678 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6683 static void RedrawAllInvisibleElementsForLenses()
6687 SCAN_PLAYFIELD(x, y)
6689 int element = Feld[x][y];
6691 if (element == EL_EMC_DRIPPER &&
6692 game.lenses_time_left > 0)
6694 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6695 TEST_DrawLevelField(x, y);
6697 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6698 game.lenses_time_left == 0)
6700 Feld[x][y] = EL_EMC_DRIPPER;
6701 TEST_DrawLevelField(x, y);
6703 else if (element == EL_INVISIBLE_STEELWALL ||
6704 element == EL_INVISIBLE_WALL ||
6705 element == EL_INVISIBLE_SAND)
6707 if (game.lenses_time_left > 0)
6708 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6710 TEST_DrawLevelField(x, y);
6712 /* uncrumble neighbour fields, if needed */
6713 if (element == EL_INVISIBLE_SAND)
6714 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6716 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6717 element == EL_INVISIBLE_WALL_ACTIVE ||
6718 element == EL_INVISIBLE_SAND_ACTIVE)
6720 if (game.lenses_time_left == 0)
6721 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6723 TEST_DrawLevelField(x, y);
6725 /* re-crumble neighbour fields, if needed */
6726 if (element == EL_INVISIBLE_SAND)
6727 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6732 static void RedrawAllInvisibleElementsForMagnifier()
6736 SCAN_PLAYFIELD(x, y)
6738 int element = Feld[x][y];
6740 if (element == EL_EMC_FAKE_GRASS &&
6741 game.magnify_time_left > 0)
6743 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6744 TEST_DrawLevelField(x, y);
6746 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6747 game.magnify_time_left == 0)
6749 Feld[x][y] = EL_EMC_FAKE_GRASS;
6750 TEST_DrawLevelField(x, y);
6752 else if (IS_GATE_GRAY(element) &&
6753 game.magnify_time_left > 0)
6755 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6756 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6757 IS_EM_GATE_GRAY(element) ?
6758 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6759 IS_EMC_GATE_GRAY(element) ?
6760 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6761 IS_DC_GATE_GRAY(element) ?
6762 EL_DC_GATE_WHITE_GRAY_ACTIVE :
6764 TEST_DrawLevelField(x, y);
6766 else if (IS_GATE_GRAY_ACTIVE(element) &&
6767 game.magnify_time_left == 0)
6769 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6770 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6771 IS_EM_GATE_GRAY_ACTIVE(element) ?
6772 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6773 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6774 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6775 IS_DC_GATE_GRAY_ACTIVE(element) ?
6776 EL_DC_GATE_WHITE_GRAY :
6778 TEST_DrawLevelField(x, y);
6783 static void ToggleLightSwitch(int x, int y)
6785 int element = Feld[x][y];
6787 game.light_time_left =
6788 (element == EL_LIGHT_SWITCH ?
6789 level.time_light * FRAMES_PER_SECOND : 0);
6791 RedrawAllLightSwitchesAndInvisibleElements();
6794 static void ActivateTimegateSwitch(int x, int y)
6798 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6800 SCAN_PLAYFIELD(xx, yy)
6802 int element = Feld[xx][yy];
6804 if (element == EL_TIMEGATE_CLOSED ||
6805 element == EL_TIMEGATE_CLOSING)
6807 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6808 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6812 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6814 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6815 TEST_DrawLevelField(xx, yy);
6822 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6823 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6825 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6829 void Impact(int x, int y)
6831 boolean last_line = (y == lev_fieldy - 1);
6832 boolean object_hit = FALSE;
6833 boolean impact = (last_line || object_hit);
6834 int element = Feld[x][y];
6835 int smashed = EL_STEELWALL;
6837 if (!last_line) /* check if element below was hit */
6839 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6842 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6843 MovDir[x][y + 1] != MV_DOWN ||
6844 MovPos[x][y + 1] <= TILEY / 2));
6846 /* do not smash moving elements that left the smashed field in time */
6847 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6848 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6851 #if USE_QUICKSAND_IMPACT_BUGFIX
6852 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6854 RemoveMovingField(x, y + 1);
6855 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6856 Feld[x][y + 2] = EL_ROCK;
6857 TEST_DrawLevelField(x, y + 2);
6862 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6864 RemoveMovingField(x, y + 1);
6865 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6866 Feld[x][y + 2] = EL_ROCK;
6867 TEST_DrawLevelField(x, y + 2);
6874 smashed = MovingOrBlocked2Element(x, y + 1);
6876 impact = (last_line || object_hit);
6879 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6881 SplashAcid(x, y + 1);
6885 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6886 /* only reset graphic animation if graphic really changes after impact */
6888 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6890 ResetGfxAnimation(x, y);
6891 TEST_DrawLevelField(x, y);
6894 if (impact && CAN_EXPLODE_IMPACT(element))
6899 else if (impact && element == EL_PEARL &&
6900 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6902 ResetGfxAnimation(x, y);
6904 Feld[x][y] = EL_PEARL_BREAKING;
6905 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6908 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6910 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6915 if (impact && element == EL_AMOEBA_DROP)
6917 if (object_hit && IS_PLAYER(x, y + 1))
6918 KillPlayerUnlessEnemyProtected(x, y + 1);
6919 else if (object_hit && smashed == EL_PENGUIN)
6923 Feld[x][y] = EL_AMOEBA_GROWING;
6924 Store[x][y] = EL_AMOEBA_WET;
6926 ResetRandomAnimationValue(x, y);
6931 if (object_hit) /* check which object was hit */
6933 if ((CAN_PASS_MAGIC_WALL(element) &&
6934 (smashed == EL_MAGIC_WALL ||
6935 smashed == EL_BD_MAGIC_WALL)) ||
6936 (CAN_PASS_DC_MAGIC_WALL(element) &&
6937 smashed == EL_DC_MAGIC_WALL))
6940 int activated_magic_wall =
6941 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6942 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6943 EL_DC_MAGIC_WALL_ACTIVE);
6945 /* activate magic wall / mill */
6946 SCAN_PLAYFIELD(xx, yy)
6948 if (Feld[xx][yy] == smashed)
6949 Feld[xx][yy] = activated_magic_wall;
6952 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6953 game.magic_wall_active = TRUE;
6955 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6956 SND_MAGIC_WALL_ACTIVATING :
6957 smashed == EL_BD_MAGIC_WALL ?
6958 SND_BD_MAGIC_WALL_ACTIVATING :
6959 SND_DC_MAGIC_WALL_ACTIVATING));
6962 if (IS_PLAYER(x, y + 1))
6964 if (CAN_SMASH_PLAYER(element))
6966 KillPlayerUnlessEnemyProtected(x, y + 1);
6970 else if (smashed == EL_PENGUIN)
6972 if (CAN_SMASH_PLAYER(element))
6978 else if (element == EL_BD_DIAMOND)
6980 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6986 else if (((element == EL_SP_INFOTRON ||
6987 element == EL_SP_ZONK) &&
6988 (smashed == EL_SP_SNIKSNAK ||
6989 smashed == EL_SP_ELECTRON ||
6990 smashed == EL_SP_DISK_ORANGE)) ||
6991 (element == EL_SP_INFOTRON &&
6992 smashed == EL_SP_DISK_YELLOW))
6997 else if (CAN_SMASH_EVERYTHING(element))
6999 if (IS_CLASSIC_ENEMY(smashed) ||
7000 CAN_EXPLODE_SMASHED(smashed))
7005 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
7007 if (smashed == EL_LAMP ||
7008 smashed == EL_LAMP_ACTIVE)
7013 else if (smashed == EL_NUT)
7015 Feld[x][y + 1] = EL_NUT_BREAKING;
7016 PlayLevelSound(x, y, SND_NUT_BREAKING);
7017 RaiseScoreElement(EL_NUT);
7020 else if (smashed == EL_PEARL)
7022 ResetGfxAnimation(x, y);
7024 Feld[x][y + 1] = EL_PEARL_BREAKING;
7025 PlayLevelSound(x, y, SND_PEARL_BREAKING);
7028 else if (smashed == EL_DIAMOND)
7030 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
7031 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
7034 else if (IS_BELT_SWITCH(smashed))
7036 ToggleBeltSwitch(x, y + 1);
7038 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
7039 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
7040 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
7041 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
7043 ToggleSwitchgateSwitch(x, y + 1);
7045 else if (smashed == EL_LIGHT_SWITCH ||
7046 smashed == EL_LIGHT_SWITCH_ACTIVE)
7048 ToggleLightSwitch(x, y + 1);
7053 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
7056 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7058 CheckElementChangeBySide(x, y + 1, smashed, element,
7059 CE_SWITCHED, CH_SIDE_TOP);
7060 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7066 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7071 /* play sound of magic wall / mill */
7073 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7074 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7075 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7077 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7078 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7079 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7080 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7081 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7082 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7087 /* play sound of object that hits the ground */
7088 if (last_line || object_hit)
7089 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7092 inline static void TurnRoundExt(int x, int y)
7104 { 0, 0 }, { 0, 0 }, { 0, 0 },
7109 int left, right, back;
7113 { MV_DOWN, MV_UP, MV_RIGHT },
7114 { MV_UP, MV_DOWN, MV_LEFT },
7116 { MV_LEFT, MV_RIGHT, MV_DOWN },
7120 { MV_RIGHT, MV_LEFT, MV_UP }
7123 int element = Feld[x][y];
7124 int move_pattern = element_info[element].move_pattern;
7126 int old_move_dir = MovDir[x][y];
7127 int left_dir = turn[old_move_dir].left;
7128 int right_dir = turn[old_move_dir].right;
7129 int back_dir = turn[old_move_dir].back;
7131 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
7132 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
7133 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
7134 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
7136 int left_x = x + left_dx, left_y = y + left_dy;
7137 int right_x = x + right_dx, right_y = y + right_dy;
7138 int move_x = x + move_dx, move_y = y + move_dy;
7142 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7144 TestIfBadThingTouchesOtherBadThing(x, y);
7146 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7147 MovDir[x][y] = right_dir;
7148 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7149 MovDir[x][y] = left_dir;
7151 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7153 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
7156 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7158 TestIfBadThingTouchesOtherBadThing(x, y);
7160 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7161 MovDir[x][y] = left_dir;
7162 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7163 MovDir[x][y] = right_dir;
7165 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7167 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
7170 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7172 TestIfBadThingTouchesOtherBadThing(x, y);
7174 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7175 MovDir[x][y] = left_dir;
7176 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7177 MovDir[x][y] = right_dir;
7179 if (MovDir[x][y] != old_move_dir)
7182 else if (element == EL_YAMYAM)
7184 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7185 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7187 if (can_turn_left && can_turn_right)
7188 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7189 else if (can_turn_left)
7190 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7191 else if (can_turn_right)
7192 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7194 MovDir[x][y] = back_dir;
7196 MovDelay[x][y] = 16 + 16 * RND(3);
7198 else if (element == EL_DARK_YAMYAM)
7200 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7202 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7205 if (can_turn_left && can_turn_right)
7206 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7207 else if (can_turn_left)
7208 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7209 else if (can_turn_right)
7210 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7212 MovDir[x][y] = back_dir;
7214 MovDelay[x][y] = 16 + 16 * RND(3);
7216 else if (element == EL_PACMAN)
7218 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7219 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7221 if (can_turn_left && can_turn_right)
7222 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7223 else if (can_turn_left)
7224 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7225 else if (can_turn_right)
7226 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7228 MovDir[x][y] = back_dir;
7230 MovDelay[x][y] = 6 + RND(40);
7232 else if (element == EL_PIG)
7234 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7235 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7236 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7237 boolean should_turn_left, should_turn_right, should_move_on;
7239 int rnd = RND(rnd_value);
7241 should_turn_left = (can_turn_left &&
7243 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7244 y + back_dy + left_dy)));
7245 should_turn_right = (can_turn_right &&
7247 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7248 y + back_dy + right_dy)));
7249 should_move_on = (can_move_on &&
7252 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7253 y + move_dy + left_dy) ||
7254 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7255 y + move_dy + right_dy)));
7257 if (should_turn_left || should_turn_right || should_move_on)
7259 if (should_turn_left && should_turn_right && should_move_on)
7260 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
7261 rnd < 2 * rnd_value / 3 ? right_dir :
7263 else if (should_turn_left && should_turn_right)
7264 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7265 else if (should_turn_left && should_move_on)
7266 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7267 else if (should_turn_right && should_move_on)
7268 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7269 else if (should_turn_left)
7270 MovDir[x][y] = left_dir;
7271 else if (should_turn_right)
7272 MovDir[x][y] = right_dir;
7273 else if (should_move_on)
7274 MovDir[x][y] = old_move_dir;
7276 else if (can_move_on && rnd > rnd_value / 8)
7277 MovDir[x][y] = old_move_dir;
7278 else if (can_turn_left && can_turn_right)
7279 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7280 else if (can_turn_left && rnd > rnd_value / 8)
7281 MovDir[x][y] = left_dir;
7282 else if (can_turn_right && rnd > rnd_value/8)
7283 MovDir[x][y] = right_dir;
7285 MovDir[x][y] = back_dir;
7287 xx = x + move_xy[MovDir[x][y]].dx;
7288 yy = y + move_xy[MovDir[x][y]].dy;
7290 if (!IN_LEV_FIELD(xx, yy) ||
7291 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7292 MovDir[x][y] = old_move_dir;
7296 else if (element == EL_DRAGON)
7298 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7299 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7300 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7302 int rnd = RND(rnd_value);
7304 if (can_move_on && rnd > rnd_value / 8)
7305 MovDir[x][y] = old_move_dir;
7306 else if (can_turn_left && can_turn_right)
7307 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7308 else if (can_turn_left && rnd > rnd_value / 8)
7309 MovDir[x][y] = left_dir;
7310 else if (can_turn_right && rnd > rnd_value / 8)
7311 MovDir[x][y] = right_dir;
7313 MovDir[x][y] = back_dir;
7315 xx = x + move_xy[MovDir[x][y]].dx;
7316 yy = y + move_xy[MovDir[x][y]].dy;
7318 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7319 MovDir[x][y] = old_move_dir;
7323 else if (element == EL_MOLE)
7325 boolean can_move_on =
7326 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7327 IS_AMOEBOID(Feld[move_x][move_y]) ||
7328 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7331 boolean can_turn_left =
7332 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7333 IS_AMOEBOID(Feld[left_x][left_y])));
7335 boolean can_turn_right =
7336 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7337 IS_AMOEBOID(Feld[right_x][right_y])));
7339 if (can_turn_left && can_turn_right)
7340 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7341 else if (can_turn_left)
7342 MovDir[x][y] = left_dir;
7344 MovDir[x][y] = right_dir;
7347 if (MovDir[x][y] != old_move_dir)
7350 else if (element == EL_BALLOON)
7352 MovDir[x][y] = game.wind_direction;
7355 else if (element == EL_SPRING)
7357 #if USE_NEW_SPRING_BUMPER
7358 if (MovDir[x][y] & MV_HORIZONTAL)
7360 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7361 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7363 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7364 ResetGfxAnimation(move_x, move_y);
7365 TEST_DrawLevelField(move_x, move_y);
7367 MovDir[x][y] = back_dir;
7369 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7370 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7371 MovDir[x][y] = MV_NONE;
7374 if (MovDir[x][y] & MV_HORIZONTAL &&
7375 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7376 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7377 MovDir[x][y] = MV_NONE;
7382 else if (element == EL_ROBOT ||
7383 element == EL_SATELLITE ||
7384 element == EL_PENGUIN ||
7385 element == EL_EMC_ANDROID)
7387 int attr_x = -1, attr_y = -1;
7398 for (i = 0; i < MAX_PLAYERS; i++)
7400 struct PlayerInfo *player = &stored_player[i];
7401 int jx = player->jx, jy = player->jy;
7403 if (!player->active)
7407 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7415 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7416 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7417 game.engine_version < VERSION_IDENT(3,1,0,0)))
7423 if (element == EL_PENGUIN)
7426 static int xy[4][2] =
7434 for (i = 0; i < NUM_DIRECTIONS; i++)
7436 int ex = x + xy[i][0];
7437 int ey = y + xy[i][1];
7439 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7440 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7441 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7442 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7451 MovDir[x][y] = MV_NONE;
7453 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7454 else if (attr_x > x)
7455 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7457 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7458 else if (attr_y > y)
7459 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7461 if (element == EL_ROBOT)
7465 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7466 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7467 Moving2Blocked(x, y, &newx, &newy);
7469 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7470 MovDelay[x][y] = 8 + 8 * !RND(3);
7472 MovDelay[x][y] = 16;
7474 else if (element == EL_PENGUIN)
7480 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7482 boolean first_horiz = RND(2);
7483 int new_move_dir = MovDir[x][y];
7486 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7487 Moving2Blocked(x, y, &newx, &newy);
7489 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7493 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7494 Moving2Blocked(x, y, &newx, &newy);
7496 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7499 MovDir[x][y] = old_move_dir;
7503 else if (element == EL_SATELLITE)
7509 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7511 boolean first_horiz = RND(2);
7512 int new_move_dir = MovDir[x][y];
7515 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7516 Moving2Blocked(x, y, &newx, &newy);
7518 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7522 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7523 Moving2Blocked(x, y, &newx, &newy);
7525 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7528 MovDir[x][y] = old_move_dir;
7532 else if (element == EL_EMC_ANDROID)
7534 static int check_pos[16] =
7536 -1, /* 0 => (invalid) */
7537 7, /* 1 => MV_LEFT */
7538 3, /* 2 => MV_RIGHT */
7539 -1, /* 3 => (invalid) */
7541 0, /* 5 => MV_LEFT | MV_UP */
7542 2, /* 6 => MV_RIGHT | MV_UP */
7543 -1, /* 7 => (invalid) */
7544 5, /* 8 => MV_DOWN */
7545 6, /* 9 => MV_LEFT | MV_DOWN */
7546 4, /* 10 => MV_RIGHT | MV_DOWN */
7547 -1, /* 11 => (invalid) */
7548 -1, /* 12 => (invalid) */
7549 -1, /* 13 => (invalid) */
7550 -1, /* 14 => (invalid) */
7551 -1, /* 15 => (invalid) */
7559 { -1, -1, MV_LEFT | MV_UP },
7561 { +1, -1, MV_RIGHT | MV_UP },
7562 { +1, 0, MV_RIGHT },
7563 { +1, +1, MV_RIGHT | MV_DOWN },
7565 { -1, +1, MV_LEFT | MV_DOWN },
7568 int start_pos, check_order;
7569 boolean can_clone = FALSE;
7572 /* check if there is any free field around current position */
7573 for (i = 0; i < 8; i++)
7575 int newx = x + check_xy[i].dx;
7576 int newy = y + check_xy[i].dy;
7578 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7586 if (can_clone) /* randomly find an element to clone */
7590 start_pos = check_pos[RND(8)];
7591 check_order = (RND(2) ? -1 : +1);
7593 for (i = 0; i < 8; i++)
7595 int pos_raw = start_pos + i * check_order;
7596 int pos = (pos_raw + 8) % 8;
7597 int newx = x + check_xy[pos].dx;
7598 int newy = y + check_xy[pos].dy;
7600 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7602 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7603 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7605 Store[x][y] = Feld[newx][newy];
7614 if (can_clone) /* randomly find a direction to move */
7618 start_pos = check_pos[RND(8)];
7619 check_order = (RND(2) ? -1 : +1);
7621 for (i = 0; i < 8; i++)
7623 int pos_raw = start_pos + i * check_order;
7624 int pos = (pos_raw + 8) % 8;
7625 int newx = x + check_xy[pos].dx;
7626 int newy = y + check_xy[pos].dy;
7627 int new_move_dir = check_xy[pos].dir;
7629 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7631 MovDir[x][y] = new_move_dir;
7632 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7641 if (can_clone) /* cloning and moving successful */
7644 /* cannot clone -- try to move towards player */
7646 start_pos = check_pos[MovDir[x][y] & 0x0f];
7647 check_order = (RND(2) ? -1 : +1);
7649 for (i = 0; i < 3; i++)
7651 /* first check start_pos, then previous/next or (next/previous) pos */
7652 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7653 int pos = (pos_raw + 8) % 8;
7654 int newx = x + check_xy[pos].dx;
7655 int newy = y + check_xy[pos].dy;
7656 int new_move_dir = check_xy[pos].dir;
7658 if (IS_PLAYER(newx, newy))
7661 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7663 MovDir[x][y] = new_move_dir;
7664 MovDelay[x][y] = level.android_move_time * 8 + 1;
7671 else if (move_pattern == MV_TURNING_LEFT ||
7672 move_pattern == MV_TURNING_RIGHT ||
7673 move_pattern == MV_TURNING_LEFT_RIGHT ||
7674 move_pattern == MV_TURNING_RIGHT_LEFT ||
7675 move_pattern == MV_TURNING_RANDOM ||
7676 move_pattern == MV_ALL_DIRECTIONS)
7678 boolean can_turn_left =
7679 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7680 boolean can_turn_right =
7681 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7683 if (element_info[element].move_stepsize == 0) /* "not moving" */
7686 if (move_pattern == MV_TURNING_LEFT)
7687 MovDir[x][y] = left_dir;
7688 else if (move_pattern == MV_TURNING_RIGHT)
7689 MovDir[x][y] = right_dir;
7690 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7691 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7692 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7693 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7694 else if (move_pattern == MV_TURNING_RANDOM)
7695 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7696 can_turn_right && !can_turn_left ? right_dir :
7697 RND(2) ? left_dir : right_dir);
7698 else if (can_turn_left && can_turn_right)
7699 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7700 else if (can_turn_left)
7701 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7702 else if (can_turn_right)
7703 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7705 MovDir[x][y] = back_dir;
7707 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7709 else if (move_pattern == MV_HORIZONTAL ||
7710 move_pattern == MV_VERTICAL)
7712 if (move_pattern & old_move_dir)
7713 MovDir[x][y] = back_dir;
7714 else if (move_pattern == MV_HORIZONTAL)
7715 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7716 else if (move_pattern == MV_VERTICAL)
7717 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7719 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7721 else if (move_pattern & MV_ANY_DIRECTION)
7723 MovDir[x][y] = move_pattern;
7724 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7726 else if (move_pattern & MV_WIND_DIRECTION)
7728 MovDir[x][y] = game.wind_direction;
7729 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7731 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7733 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7734 MovDir[x][y] = left_dir;
7735 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7736 MovDir[x][y] = right_dir;
7738 if (MovDir[x][y] != old_move_dir)
7739 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7741 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7743 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7744 MovDir[x][y] = right_dir;
7745 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7746 MovDir[x][y] = left_dir;
7748 if (MovDir[x][y] != old_move_dir)
7749 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7751 else if (move_pattern == MV_TOWARDS_PLAYER ||
7752 move_pattern == MV_AWAY_FROM_PLAYER)
7754 int attr_x = -1, attr_y = -1;
7756 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7767 for (i = 0; i < MAX_PLAYERS; i++)
7769 struct PlayerInfo *player = &stored_player[i];
7770 int jx = player->jx, jy = player->jy;
7772 if (!player->active)
7776 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7784 MovDir[x][y] = MV_NONE;
7786 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7787 else if (attr_x > x)
7788 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7790 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7791 else if (attr_y > y)
7792 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7794 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7796 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7798 boolean first_horiz = RND(2);
7799 int new_move_dir = MovDir[x][y];
7801 if (element_info[element].move_stepsize == 0) /* "not moving" */
7803 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7804 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7810 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7811 Moving2Blocked(x, y, &newx, &newy);
7813 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7817 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7818 Moving2Blocked(x, y, &newx, &newy);
7820 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7823 MovDir[x][y] = old_move_dir;
7826 else if (move_pattern == MV_WHEN_PUSHED ||
7827 move_pattern == MV_WHEN_DROPPED)
7829 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7830 MovDir[x][y] = MV_NONE;
7834 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7836 static int test_xy[7][2] =
7846 static int test_dir[7] =
7856 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7857 int move_preference = -1000000; /* start with very low preference */
7858 int new_move_dir = MV_NONE;
7859 int start_test = RND(4);
7862 for (i = 0; i < NUM_DIRECTIONS; i++)
7864 int move_dir = test_dir[start_test + i];
7865 int move_dir_preference;
7867 xx = x + test_xy[start_test + i][0];
7868 yy = y + test_xy[start_test + i][1];
7870 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7871 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7873 new_move_dir = move_dir;
7878 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7881 move_dir_preference = -1 * RunnerVisit[xx][yy];
7882 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7883 move_dir_preference = PlayerVisit[xx][yy];
7885 if (move_dir_preference > move_preference)
7887 /* prefer field that has not been visited for the longest time */
7888 move_preference = move_dir_preference;
7889 new_move_dir = move_dir;
7891 else if (move_dir_preference == move_preference &&
7892 move_dir == old_move_dir)
7894 /* prefer last direction when all directions are preferred equally */
7895 move_preference = move_dir_preference;
7896 new_move_dir = move_dir;
7900 MovDir[x][y] = new_move_dir;
7901 if (old_move_dir != new_move_dir)
7902 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7906 static void TurnRound(int x, int y)
7908 int direction = MovDir[x][y];
7912 GfxDir[x][y] = MovDir[x][y];
7914 if (direction != MovDir[x][y])
7918 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7920 ResetGfxFrame(x, y, FALSE);
7923 static boolean JustBeingPushed(int x, int y)
7927 for (i = 0; i < MAX_PLAYERS; i++)
7929 struct PlayerInfo *player = &stored_player[i];
7931 if (player->active && player->is_pushing && player->MovPos)
7933 int next_jx = player->jx + (player->jx - player->last_jx);
7934 int next_jy = player->jy + (player->jy - player->last_jy);
7936 if (x == next_jx && y == next_jy)
7944 void StartMoving(int x, int y)
7946 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7947 int element = Feld[x][y];
7952 if (MovDelay[x][y] == 0)
7953 GfxAction[x][y] = ACTION_DEFAULT;
7955 if (CAN_FALL(element) && y < lev_fieldy - 1)
7957 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7958 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7959 if (JustBeingPushed(x, y))
7962 if (element == EL_QUICKSAND_FULL)
7964 if (IS_FREE(x, y + 1))
7966 InitMovingField(x, y, MV_DOWN);
7967 started_moving = TRUE;
7969 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7970 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7971 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7972 Store[x][y] = EL_ROCK;
7974 Store[x][y] = EL_ROCK;
7977 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7979 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7981 if (!MovDelay[x][y])
7983 MovDelay[x][y] = TILEY + 1;
7985 ResetGfxAnimation(x, y);
7986 ResetGfxAnimation(x, y + 1);
7991 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7992 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7999 Feld[x][y] = EL_QUICKSAND_EMPTY;
8000 Feld[x][y + 1] = EL_QUICKSAND_FULL;
8001 Store[x][y + 1] = Store[x][y];
8004 PlayLevelSoundAction(x, y, ACTION_FILLING);
8006 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8008 if (!MovDelay[x][y])
8010 MovDelay[x][y] = TILEY + 1;
8012 ResetGfxAnimation(x, y);
8013 ResetGfxAnimation(x, y + 1);
8018 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
8019 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8026 Feld[x][y] = EL_QUICKSAND_EMPTY;
8027 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8028 Store[x][y + 1] = Store[x][y];
8031 PlayLevelSoundAction(x, y, ACTION_FILLING);
8034 else if (element == EL_QUICKSAND_FAST_FULL)
8036 if (IS_FREE(x, y + 1))
8038 InitMovingField(x, y, MV_DOWN);
8039 started_moving = TRUE;
8041 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
8042 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8043 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8044 Store[x][y] = EL_ROCK;
8046 Store[x][y] = EL_ROCK;
8049 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8051 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8053 if (!MovDelay[x][y])
8055 MovDelay[x][y] = TILEY + 1;
8057 ResetGfxAnimation(x, y);
8058 ResetGfxAnimation(x, y + 1);
8063 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8064 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8071 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8072 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8073 Store[x][y + 1] = Store[x][y];
8076 PlayLevelSoundAction(x, y, ACTION_FILLING);
8078 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8080 if (!MovDelay[x][y])
8082 MovDelay[x][y] = TILEY + 1;
8084 ResetGfxAnimation(x, y);
8085 ResetGfxAnimation(x, y + 1);
8090 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8091 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8098 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8099 Feld[x][y + 1] = EL_QUICKSAND_FULL;
8100 Store[x][y + 1] = Store[x][y];
8103 PlayLevelSoundAction(x, y, ACTION_FILLING);
8106 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8107 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8109 InitMovingField(x, y, MV_DOWN);
8110 started_moving = TRUE;
8112 Feld[x][y] = EL_QUICKSAND_FILLING;
8113 Store[x][y] = element;
8115 PlayLevelSoundAction(x, y, ACTION_FILLING);
8117 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8118 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8120 InitMovingField(x, y, MV_DOWN);
8121 started_moving = TRUE;
8123 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
8124 Store[x][y] = element;
8126 PlayLevelSoundAction(x, y, ACTION_FILLING);
8128 else if (element == EL_MAGIC_WALL_FULL)
8130 if (IS_FREE(x, y + 1))
8132 InitMovingField(x, y, MV_DOWN);
8133 started_moving = TRUE;
8135 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
8136 Store[x][y] = EL_CHANGED(Store[x][y]);
8138 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8140 if (!MovDelay[x][y])
8141 MovDelay[x][y] = TILEY/4 + 1;
8150 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
8151 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
8152 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8156 else if (element == EL_BD_MAGIC_WALL_FULL)
8158 if (IS_FREE(x, y + 1))
8160 InitMovingField(x, y, MV_DOWN);
8161 started_moving = TRUE;
8163 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8164 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8166 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8168 if (!MovDelay[x][y])
8169 MovDelay[x][y] = TILEY/4 + 1;
8178 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8179 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8180 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8184 else if (element == EL_DC_MAGIC_WALL_FULL)
8186 if (IS_FREE(x, y + 1))
8188 InitMovingField(x, y, MV_DOWN);
8189 started_moving = TRUE;
8191 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8192 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8194 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8196 if (!MovDelay[x][y])
8197 MovDelay[x][y] = TILEY/4 + 1;
8206 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8207 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8208 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8212 else if ((CAN_PASS_MAGIC_WALL(element) &&
8213 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8214 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8215 (CAN_PASS_DC_MAGIC_WALL(element) &&
8216 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8219 InitMovingField(x, y, MV_DOWN);
8220 started_moving = TRUE;
8223 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8224 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8225 EL_DC_MAGIC_WALL_FILLING);
8226 Store[x][y] = element;
8228 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8230 SplashAcid(x, y + 1);
8232 InitMovingField(x, y, MV_DOWN);
8233 started_moving = TRUE;
8235 Store[x][y] = EL_ACID;
8238 #if USE_FIX_IMPACT_COLLISION
8239 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8240 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8242 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8243 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8245 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8246 CAN_FALL(element) && WasJustFalling[x][y] &&
8247 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8249 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8250 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8251 (Feld[x][y + 1] == EL_BLOCKED)))
8253 /* this is needed for a special case not covered by calling "Impact()"
8254 from "ContinueMoving()": if an element moves to a tile directly below
8255 another element which was just falling on that tile (which was empty
8256 in the previous frame), the falling element above would just stop
8257 instead of smashing the element below (in previous version, the above
8258 element was just checked for "moving" instead of "falling", resulting
8259 in incorrect smashes caused by horizontal movement of the above
8260 element; also, the case of the player being the element to smash was
8261 simply not covered here... :-/ ) */
8263 CheckCollision[x][y] = 0;
8264 CheckImpact[x][y] = 0;
8268 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8270 if (MovDir[x][y] == MV_NONE)
8272 InitMovingField(x, y, MV_DOWN);
8273 started_moving = TRUE;
8276 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8278 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8279 MovDir[x][y] = MV_DOWN;
8281 InitMovingField(x, y, MV_DOWN);
8282 started_moving = TRUE;
8284 else if (element == EL_AMOEBA_DROP)
8286 Feld[x][y] = EL_AMOEBA_GROWING;
8287 Store[x][y] = EL_AMOEBA_WET;
8289 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8290 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8291 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8292 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8294 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
8295 (IS_FREE(x - 1, y + 1) ||
8296 Feld[x - 1][y + 1] == EL_ACID));
8297 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8298 (IS_FREE(x + 1, y + 1) ||
8299 Feld[x + 1][y + 1] == EL_ACID));
8300 boolean can_fall_any = (can_fall_left || can_fall_right);
8301 boolean can_fall_both = (can_fall_left && can_fall_right);
8302 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8304 #if USE_NEW_ALL_SLIPPERY
8305 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8307 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8308 can_fall_right = FALSE;
8309 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8310 can_fall_left = FALSE;
8311 else if (slippery_type == SLIPPERY_ONLY_LEFT)
8312 can_fall_right = FALSE;
8313 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8314 can_fall_left = FALSE;
8316 can_fall_any = (can_fall_left || can_fall_right);
8317 can_fall_both = FALSE;
8320 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8322 if (slippery_type == SLIPPERY_ONLY_LEFT)
8323 can_fall_right = FALSE;
8324 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8325 can_fall_left = FALSE;
8326 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8327 can_fall_right = FALSE;
8328 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8329 can_fall_left = FALSE;
8331 can_fall_any = (can_fall_left || can_fall_right);
8332 can_fall_both = (can_fall_left && can_fall_right);
8336 #if USE_NEW_ALL_SLIPPERY
8338 #if USE_NEW_SP_SLIPPERY
8339 /* !!! better use the same properties as for custom elements here !!! */
8340 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8341 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8343 can_fall_right = FALSE; /* slip down on left side */
8344 can_fall_both = FALSE;
8349 #if USE_NEW_ALL_SLIPPERY
8352 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8353 can_fall_right = FALSE; /* slip down on left side */
8355 can_fall_left = !(can_fall_right = RND(2));
8357 can_fall_both = FALSE;
8362 if (game.emulation == EMU_BOULDERDASH ||
8363 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8364 can_fall_right = FALSE; /* slip down on left side */
8366 can_fall_left = !(can_fall_right = RND(2));
8368 can_fall_both = FALSE;
8374 /* if not determined otherwise, prefer left side for slipping down */
8375 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8376 started_moving = TRUE;
8380 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8382 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8385 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
8386 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8387 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8388 int belt_dir = game.belt_dir[belt_nr];
8390 if ((belt_dir == MV_LEFT && left_is_free) ||
8391 (belt_dir == MV_RIGHT && right_is_free))
8393 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8395 InitMovingField(x, y, belt_dir);
8396 started_moving = TRUE;
8398 Pushed[x][y] = TRUE;
8399 Pushed[nextx][y] = TRUE;
8401 GfxAction[x][y] = ACTION_DEFAULT;
8405 MovDir[x][y] = 0; /* if element was moving, stop it */
8410 /* not "else if" because of elements that can fall and move (EL_SPRING) */
8412 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8414 if (CAN_MOVE(element) && !started_moving)
8417 int move_pattern = element_info[element].move_pattern;
8422 if (MovDir[x][y] == MV_NONE)
8424 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8425 x, y, element, element_info[element].token_name);
8426 printf("StartMoving(): This should never happen!\n");
8431 Moving2Blocked(x, y, &newx, &newy);
8433 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8436 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8437 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8439 WasJustMoving[x][y] = 0;
8440 CheckCollision[x][y] = 0;
8442 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8444 if (Feld[x][y] != element) /* element has changed */
8448 if (!MovDelay[x][y]) /* start new movement phase */
8450 /* all objects that can change their move direction after each step
8451 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8453 if (element != EL_YAMYAM &&
8454 element != EL_DARK_YAMYAM &&
8455 element != EL_PACMAN &&
8456 !(move_pattern & MV_ANY_DIRECTION) &&
8457 move_pattern != MV_TURNING_LEFT &&
8458 move_pattern != MV_TURNING_RIGHT &&
8459 move_pattern != MV_TURNING_LEFT_RIGHT &&
8460 move_pattern != MV_TURNING_RIGHT_LEFT &&
8461 move_pattern != MV_TURNING_RANDOM)
8465 if (MovDelay[x][y] && (element == EL_BUG ||
8466 element == EL_SPACESHIP ||
8467 element == EL_SP_SNIKSNAK ||
8468 element == EL_SP_ELECTRON ||
8469 element == EL_MOLE))
8470 TEST_DrawLevelField(x, y);
8474 if (MovDelay[x][y]) /* wait some time before next movement */
8478 if (element == EL_ROBOT ||
8479 element == EL_YAMYAM ||
8480 element == EL_DARK_YAMYAM)
8482 DrawLevelElementAnimationIfNeeded(x, y, element);
8483 PlayLevelSoundAction(x, y, ACTION_WAITING);
8485 else if (element == EL_SP_ELECTRON)
8486 DrawLevelElementAnimationIfNeeded(x, y, element);
8487 else if (element == EL_DRAGON)
8490 int dir = MovDir[x][y];
8491 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8492 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
8493 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
8494 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
8495 dir == MV_UP ? IMG_FLAMES_1_UP :
8496 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8497 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8499 GfxAction[x][y] = ACTION_ATTACKING;
8501 if (IS_PLAYER(x, y))
8502 DrawPlayerField(x, y);
8504 TEST_DrawLevelField(x, y);
8506 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8508 for (i = 1; i <= 3; i++)
8510 int xx = x + i * dx;
8511 int yy = y + i * dy;
8512 int sx = SCREENX(xx);
8513 int sy = SCREENY(yy);
8514 int flame_graphic = graphic + (i - 1);
8516 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8521 int flamed = MovingOrBlocked2Element(xx, yy);
8525 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8527 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8528 RemoveMovingField(xx, yy);
8530 RemoveField(xx, yy);
8532 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8535 RemoveMovingField(xx, yy);
8538 ChangeDelay[xx][yy] = 0;
8540 Feld[xx][yy] = EL_FLAMES;
8542 if (IN_SCR_FIELD(sx, sy))
8544 TEST_DrawLevelFieldCrumbled(xx, yy);
8545 DrawGraphic(sx, sy, flame_graphic, frame);
8550 if (Feld[xx][yy] == EL_FLAMES)
8551 Feld[xx][yy] = EL_EMPTY;
8552 TEST_DrawLevelField(xx, yy);
8557 if (MovDelay[x][y]) /* element still has to wait some time */
8559 PlayLevelSoundAction(x, y, ACTION_WAITING);
8565 /* now make next step */
8567 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8569 if (DONT_COLLIDE_WITH(element) &&
8570 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8571 !PLAYER_ENEMY_PROTECTED(newx, newy))
8573 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8578 else if (CAN_MOVE_INTO_ACID(element) &&
8579 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8580 !IS_MV_DIAGONAL(MovDir[x][y]) &&
8581 (MovDir[x][y] == MV_DOWN ||
8582 game.engine_version >= VERSION_IDENT(3,1,0,0)))
8584 SplashAcid(newx, newy);
8585 Store[x][y] = EL_ACID;
8587 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8589 if (Feld[newx][newy] == EL_EXIT_OPEN ||
8590 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8591 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8592 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8595 TEST_DrawLevelField(x, y);
8597 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8598 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8599 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8601 local_player->friends_still_needed--;
8602 if (!local_player->friends_still_needed &&
8603 !local_player->GameOver && AllPlayersGone)
8604 PlayerWins(local_player);
8608 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8610 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8611 TEST_DrawLevelField(newx, newy);
8613 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8615 else if (!IS_FREE(newx, newy))
8617 GfxAction[x][y] = ACTION_WAITING;
8619 if (IS_PLAYER(x, y))
8620 DrawPlayerField(x, y);
8622 TEST_DrawLevelField(x, y);
8627 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8629 if (IS_FOOD_PIG(Feld[newx][newy]))
8631 if (IS_MOVING(newx, newy))
8632 RemoveMovingField(newx, newy);
8635 Feld[newx][newy] = EL_EMPTY;
8636 TEST_DrawLevelField(newx, newy);
8639 PlayLevelSound(x, y, SND_PIG_DIGGING);
8641 else if (!IS_FREE(newx, newy))
8643 if (IS_PLAYER(x, y))
8644 DrawPlayerField(x, y);
8646 TEST_DrawLevelField(x, y);
8651 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8653 if (Store[x][y] != EL_EMPTY)
8655 boolean can_clone = FALSE;
8658 /* check if element to clone is still there */
8659 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8661 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8669 /* cannot clone or target field not free anymore -- do not clone */
8670 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8671 Store[x][y] = EL_EMPTY;
8674 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8676 if (IS_MV_DIAGONAL(MovDir[x][y]))
8678 int diagonal_move_dir = MovDir[x][y];
8679 int stored = Store[x][y];
8680 int change_delay = 8;
8683 /* android is moving diagonally */
8685 CreateField(x, y, EL_DIAGONAL_SHRINKING);
8687 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8688 GfxElement[x][y] = EL_EMC_ANDROID;
8689 GfxAction[x][y] = ACTION_SHRINKING;
8690 GfxDir[x][y] = diagonal_move_dir;
8691 ChangeDelay[x][y] = change_delay;
8693 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8696 DrawLevelGraphicAnimation(x, y, graphic);
8697 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8699 if (Feld[newx][newy] == EL_ACID)
8701 SplashAcid(newx, newy);
8706 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8708 Store[newx][newy] = EL_EMC_ANDROID;
8709 GfxElement[newx][newy] = EL_EMC_ANDROID;
8710 GfxAction[newx][newy] = ACTION_GROWING;
8711 GfxDir[newx][newy] = diagonal_move_dir;
8712 ChangeDelay[newx][newy] = change_delay;
8714 graphic = el_act_dir2img(GfxElement[newx][newy],
8715 GfxAction[newx][newy], GfxDir[newx][newy]);
8717 DrawLevelGraphicAnimation(newx, newy, graphic);
8718 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8724 Feld[newx][newy] = EL_EMPTY;
8725 TEST_DrawLevelField(newx, newy);
8727 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8730 else if (!IS_FREE(newx, newy))
8733 if (IS_PLAYER(x, y))
8734 DrawPlayerField(x, y);
8736 TEST_DrawLevelField(x, y);
8742 else if (IS_CUSTOM_ELEMENT(element) &&
8743 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8746 if (!DigFieldByCE(newx, newy, element))
8749 int new_element = Feld[newx][newy];
8751 if (!IS_FREE(newx, newy))
8753 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8754 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8757 /* no element can dig solid indestructible elements */
8758 if (IS_INDESTRUCTIBLE(new_element) &&
8759 !IS_DIGGABLE(new_element) &&
8760 !IS_COLLECTIBLE(new_element))
8763 if (AmoebaNr[newx][newy] &&
8764 (new_element == EL_AMOEBA_FULL ||
8765 new_element == EL_BD_AMOEBA ||
8766 new_element == EL_AMOEBA_GROWING))
8768 AmoebaCnt[AmoebaNr[newx][newy]]--;
8769 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8772 if (IS_MOVING(newx, newy))
8773 RemoveMovingField(newx, newy);
8776 RemoveField(newx, newy);
8777 TEST_DrawLevelField(newx, newy);
8780 /* if digged element was about to explode, prevent the explosion */
8781 ExplodeField[newx][newy] = EX_TYPE_NONE;
8783 PlayLevelSoundAction(x, y, action);
8786 Store[newx][newy] = EL_EMPTY;
8789 /* this makes it possible to leave the removed element again */
8790 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8791 Store[newx][newy] = new_element;
8793 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8795 int move_leave_element = element_info[element].move_leave_element;
8797 /* this makes it possible to leave the removed element again */
8798 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8799 new_element : move_leave_element);
8805 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8807 RunnerVisit[x][y] = FrameCounter;
8808 PlayerVisit[x][y] /= 8; /* expire player visit path */
8811 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8813 if (!IS_FREE(newx, newy))
8815 if (IS_PLAYER(x, y))
8816 DrawPlayerField(x, y);
8818 TEST_DrawLevelField(x, y);
8824 boolean wanna_flame = !RND(10);
8825 int dx = newx - x, dy = newy - y;
8826 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8827 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8828 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8829 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8830 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8831 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8834 IS_CLASSIC_ENEMY(element1) ||
8835 IS_CLASSIC_ENEMY(element2)) &&
8836 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8837 element1 != EL_FLAMES && element2 != EL_FLAMES)
8839 ResetGfxAnimation(x, y);
8840 GfxAction[x][y] = ACTION_ATTACKING;
8842 if (IS_PLAYER(x, y))
8843 DrawPlayerField(x, y);
8845 TEST_DrawLevelField(x, y);
8847 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8849 MovDelay[x][y] = 50;
8853 RemoveField(newx, newy);
8855 Feld[newx][newy] = EL_FLAMES;
8856 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8859 RemoveField(newx1, newy1);
8861 Feld[newx1][newy1] = EL_FLAMES;
8863 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8866 RemoveField(newx2, newy2);
8868 Feld[newx2][newy2] = EL_FLAMES;
8875 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8876 Feld[newx][newy] == EL_DIAMOND)
8878 if (IS_MOVING(newx, newy))
8879 RemoveMovingField(newx, newy);
8882 Feld[newx][newy] = EL_EMPTY;
8883 TEST_DrawLevelField(newx, newy);
8886 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8888 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8889 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8891 if (AmoebaNr[newx][newy])
8893 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8894 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8895 Feld[newx][newy] == EL_BD_AMOEBA)
8896 AmoebaCnt[AmoebaNr[newx][newy]]--;
8901 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8903 RemoveMovingField(newx, newy);
8906 if (IS_MOVING(newx, newy))
8908 RemoveMovingField(newx, newy);
8913 Feld[newx][newy] = EL_EMPTY;
8914 TEST_DrawLevelField(newx, newy);
8917 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8919 else if ((element == EL_PACMAN || element == EL_MOLE)
8920 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8922 if (AmoebaNr[newx][newy])
8924 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8925 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8926 Feld[newx][newy] == EL_BD_AMOEBA)
8927 AmoebaCnt[AmoebaNr[newx][newy]]--;
8930 if (element == EL_MOLE)
8932 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8933 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8935 ResetGfxAnimation(x, y);
8936 GfxAction[x][y] = ACTION_DIGGING;
8937 TEST_DrawLevelField(x, y);
8939 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8941 return; /* wait for shrinking amoeba */
8943 else /* element == EL_PACMAN */
8945 Feld[newx][newy] = EL_EMPTY;
8946 TEST_DrawLevelField(newx, newy);
8947 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8950 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8951 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8952 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8954 /* wait for shrinking amoeba to completely disappear */
8957 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8959 /* object was running against a wall */
8964 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8965 if (move_pattern & MV_ANY_DIRECTION &&
8966 move_pattern == MovDir[x][y])
8968 int blocking_element =
8969 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8971 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8974 element = Feld[x][y]; /* element might have changed */
8978 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8979 DrawLevelElementAnimation(x, y, element);
8981 if (DONT_TOUCH(element))
8982 TestIfBadThingTouchesPlayer(x, y);
8987 InitMovingField(x, y, MovDir[x][y]);
8989 PlayLevelSoundAction(x, y, ACTION_MOVING);
8993 ContinueMoving(x, y);
8996 void ContinueMoving(int x, int y)
8998 int element = Feld[x][y];
8999 struct ElementInfo *ei = &element_info[element];
9000 int direction = MovDir[x][y];
9001 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9002 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9003 int newx = x + dx, newy = y + dy;
9004 int stored = Store[x][y];
9005 int stored_new = Store[newx][newy];
9006 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
9007 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
9008 boolean last_line = (newy == lev_fieldy - 1);
9010 MovPos[x][y] += getElementMoveStepsize(x, y);
9012 if (pushed_by_player) /* special case: moving object pushed by player */
9013 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
9015 if (ABS(MovPos[x][y]) < TILEX)
9018 int ee = Feld[x][y];
9019 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9020 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
9022 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
9023 x, y, ABS(MovPos[x][y]),
9025 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
9028 TEST_DrawLevelField(x, y);
9030 return; /* element is still moving */
9033 /* element reached destination field */
9035 Feld[x][y] = EL_EMPTY;
9036 Feld[newx][newy] = element;
9037 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
9039 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
9041 element = Feld[newx][newy] = EL_ACID;
9043 else if (element == EL_MOLE)
9045 Feld[x][y] = EL_SAND;
9047 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9049 else if (element == EL_QUICKSAND_FILLING)
9051 element = Feld[newx][newy] = get_next_element(element);
9052 Store[newx][newy] = Store[x][y];
9054 else if (element == EL_QUICKSAND_EMPTYING)
9056 Feld[x][y] = get_next_element(element);
9057 element = Feld[newx][newy] = Store[x][y];
9059 else if (element == EL_QUICKSAND_FAST_FILLING)
9061 element = Feld[newx][newy] = get_next_element(element);
9062 Store[newx][newy] = Store[x][y];
9064 else if (element == EL_QUICKSAND_FAST_EMPTYING)
9066 Feld[x][y] = get_next_element(element);
9067 element = Feld[newx][newy] = Store[x][y];
9069 else if (element == EL_MAGIC_WALL_FILLING)
9071 element = Feld[newx][newy] = get_next_element(element);
9072 if (!game.magic_wall_active)
9073 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
9074 Store[newx][newy] = Store[x][y];
9076 else if (element == EL_MAGIC_WALL_EMPTYING)
9078 Feld[x][y] = get_next_element(element);
9079 if (!game.magic_wall_active)
9080 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9081 element = Feld[newx][newy] = Store[x][y];
9083 #if USE_NEW_CUSTOM_VALUE
9084 InitField(newx, newy, FALSE);
9087 else if (element == EL_BD_MAGIC_WALL_FILLING)
9089 element = Feld[newx][newy] = get_next_element(element);
9090 if (!game.magic_wall_active)
9091 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
9092 Store[newx][newy] = Store[x][y];
9094 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
9096 Feld[x][y] = get_next_element(element);
9097 if (!game.magic_wall_active)
9098 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9099 element = Feld[newx][newy] = Store[x][y];
9101 #if USE_NEW_CUSTOM_VALUE
9102 InitField(newx, newy, FALSE);
9105 else if (element == EL_DC_MAGIC_WALL_FILLING)
9107 element = Feld[newx][newy] = get_next_element(element);
9108 if (!game.magic_wall_active)
9109 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
9110 Store[newx][newy] = Store[x][y];
9112 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
9114 Feld[x][y] = get_next_element(element);
9115 if (!game.magic_wall_active)
9116 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
9117 element = Feld[newx][newy] = Store[x][y];
9119 #if USE_NEW_CUSTOM_VALUE
9120 InitField(newx, newy, FALSE);
9123 else if (element == EL_AMOEBA_DROPPING)
9125 Feld[x][y] = get_next_element(element);
9126 element = Feld[newx][newy] = Store[x][y];
9128 else if (element == EL_SOKOBAN_OBJECT)
9131 Feld[x][y] = Back[x][y];
9133 if (Back[newx][newy])
9134 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
9136 Back[x][y] = Back[newx][newy] = 0;
9139 Store[x][y] = EL_EMPTY;
9144 MovDelay[newx][newy] = 0;
9146 if (CAN_CHANGE_OR_HAS_ACTION(element))
9148 /* copy element change control values to new field */
9149 ChangeDelay[newx][newy] = ChangeDelay[x][y];
9150 ChangePage[newx][newy] = ChangePage[x][y];
9151 ChangeCount[newx][newy] = ChangeCount[x][y];
9152 ChangeEvent[newx][newy] = ChangeEvent[x][y];
9155 #if USE_NEW_CUSTOM_VALUE
9156 CustomValue[newx][newy] = CustomValue[x][y];
9159 ChangeDelay[x][y] = 0;
9160 ChangePage[x][y] = -1;
9161 ChangeCount[x][y] = 0;
9162 ChangeEvent[x][y] = -1;
9164 #if USE_NEW_CUSTOM_VALUE
9165 CustomValue[x][y] = 0;
9168 /* copy animation control values to new field */
9169 GfxFrame[newx][newy] = GfxFrame[x][y];
9170 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
9171 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
9172 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
9174 Pushed[x][y] = Pushed[newx][newy] = FALSE;
9176 /* some elements can leave other elements behind after moving */
9178 if (ei->move_leave_element != EL_EMPTY &&
9179 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9180 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9182 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
9183 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9184 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9187 int move_leave_element = ei->move_leave_element;
9191 /* this makes it possible to leave the removed element again */
9192 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9193 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9195 /* this makes it possible to leave the removed element again */
9196 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9197 move_leave_element = stored;
9200 /* this makes it possible to leave the removed element again */
9201 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
9202 ei->move_leave_element == EL_TRIGGER_ELEMENT)
9203 move_leave_element = stored;
9206 Feld[x][y] = move_leave_element;
9208 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
9209 MovDir[x][y] = direction;
9211 InitField(x, y, FALSE);
9213 if (GFX_CRUMBLED(Feld[x][y]))
9214 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9216 if (ELEM_IS_PLAYER(move_leave_element))
9217 RelocatePlayer(x, y, move_leave_element);
9220 /* do this after checking for left-behind element */
9221 ResetGfxAnimation(x, y); /* reset animation values for old field */
9223 if (!CAN_MOVE(element) ||
9224 (CAN_FALL(element) && direction == MV_DOWN &&
9225 (element == EL_SPRING ||
9226 element_info[element].move_pattern == MV_WHEN_PUSHED ||
9227 element_info[element].move_pattern == MV_WHEN_DROPPED)))
9228 GfxDir[x][y] = MovDir[newx][newy] = 0;
9230 TEST_DrawLevelField(x, y);
9231 TEST_DrawLevelField(newx, newy);
9233 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
9235 /* prevent pushed element from moving on in pushed direction */
9236 if (pushed_by_player && CAN_MOVE(element) &&
9237 element_info[element].move_pattern & MV_ANY_DIRECTION &&
9238 !(element_info[element].move_pattern & direction))
9239 TurnRound(newx, newy);
9241 /* prevent elements on conveyor belt from moving on in last direction */
9242 if (pushed_by_conveyor && CAN_FALL(element) &&
9243 direction & MV_HORIZONTAL)
9244 MovDir[newx][newy] = 0;
9246 if (!pushed_by_player)
9248 int nextx = newx + dx, nexty = newy + dy;
9249 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9251 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9253 if (CAN_FALL(element) && direction == MV_DOWN)
9254 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9256 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9257 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9259 #if USE_FIX_IMPACT_COLLISION
9260 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9261 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9265 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
9267 TestIfBadThingTouchesPlayer(newx, newy);
9268 TestIfBadThingTouchesFriend(newx, newy);
9270 if (!IS_CUSTOM_ELEMENT(element))
9271 TestIfBadThingTouchesOtherBadThing(newx, newy);
9273 else if (element == EL_PENGUIN)
9274 TestIfFriendTouchesBadThing(newx, newy);
9276 if (DONT_GET_HIT_BY(element))
9278 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9281 /* give the player one last chance (one more frame) to move away */
9282 if (CAN_FALL(element) && direction == MV_DOWN &&
9283 (last_line || (!IS_FREE(x, newy + 1) &&
9284 (!IS_PLAYER(x, newy + 1) ||
9285 game.engine_version < VERSION_IDENT(3,1,1,0)))))
9288 if (pushed_by_player && !game.use_change_when_pushing_bug)
9290 int push_side = MV_DIR_OPPOSITE(direction);
9291 struct PlayerInfo *player = PLAYERINFO(x, y);
9293 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9294 player->index_bit, push_side);
9295 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9296 player->index_bit, push_side);
9299 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
9300 MovDelay[newx][newy] = 1;
9302 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9304 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
9307 if (ChangePage[newx][newy] != -1) /* delayed change */
9309 int page = ChangePage[newx][newy];
9310 struct ElementChangeInfo *change = &ei->change_page[page];
9312 ChangePage[newx][newy] = -1;
9314 if (change->can_change)
9316 if (ChangeElement(newx, newy, element, page))
9318 if (change->post_change_function)
9319 change->post_change_function(newx, newy);
9323 if (change->has_action)
9324 ExecuteCustomElementAction(newx, newy, element, page);
9328 TestIfElementHitsCustomElement(newx, newy, direction);
9329 TestIfPlayerTouchesCustomElement(newx, newy);
9330 TestIfElementTouchesCustomElement(newx, newy);
9332 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9333 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9334 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9335 MV_DIR_OPPOSITE(direction));
9338 int AmoebeNachbarNr(int ax, int ay)
9341 int element = Feld[ax][ay];
9343 static int xy[4][2] =
9351 for (i = 0; i < NUM_DIRECTIONS; i++)
9353 int x = ax + xy[i][0];
9354 int y = ay + xy[i][1];
9356 if (!IN_LEV_FIELD(x, y))
9359 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9360 group_nr = AmoebaNr[x][y];
9366 void AmoebenVereinigen(int ax, int ay)
9368 int i, x, y, xx, yy;
9369 int new_group_nr = AmoebaNr[ax][ay];
9370 static int xy[4][2] =
9378 if (new_group_nr == 0)
9381 for (i = 0; i < NUM_DIRECTIONS; i++)
9386 if (!IN_LEV_FIELD(x, y))
9389 if ((Feld[x][y] == EL_AMOEBA_FULL ||
9390 Feld[x][y] == EL_BD_AMOEBA ||
9391 Feld[x][y] == EL_AMOEBA_DEAD) &&
9392 AmoebaNr[x][y] != new_group_nr)
9394 int old_group_nr = AmoebaNr[x][y];
9396 if (old_group_nr == 0)
9399 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9400 AmoebaCnt[old_group_nr] = 0;
9401 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9402 AmoebaCnt2[old_group_nr] = 0;
9404 SCAN_PLAYFIELD(xx, yy)
9406 if (AmoebaNr[xx][yy] == old_group_nr)
9407 AmoebaNr[xx][yy] = new_group_nr;
9413 void AmoebeUmwandeln(int ax, int ay)
9417 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9419 int group_nr = AmoebaNr[ax][ay];
9424 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9425 printf("AmoebeUmwandeln(): This should never happen!\n");
9430 SCAN_PLAYFIELD(x, y)
9432 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9435 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9439 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9440 SND_AMOEBA_TURNING_TO_GEM :
9441 SND_AMOEBA_TURNING_TO_ROCK));
9446 static int xy[4][2] =
9454 for (i = 0; i < NUM_DIRECTIONS; i++)
9459 if (!IN_LEV_FIELD(x, y))
9462 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9464 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9465 SND_AMOEBA_TURNING_TO_GEM :
9466 SND_AMOEBA_TURNING_TO_ROCK));
9473 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9476 int group_nr = AmoebaNr[ax][ay];
9477 boolean done = FALSE;
9482 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9483 printf("AmoebeUmwandelnBD(): This should never happen!\n");
9488 SCAN_PLAYFIELD(x, y)
9490 if (AmoebaNr[x][y] == group_nr &&
9491 (Feld[x][y] == EL_AMOEBA_DEAD ||
9492 Feld[x][y] == EL_BD_AMOEBA ||
9493 Feld[x][y] == EL_AMOEBA_GROWING))
9496 Feld[x][y] = new_element;
9497 InitField(x, y, FALSE);
9498 TEST_DrawLevelField(x, y);
9504 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9505 SND_BD_AMOEBA_TURNING_TO_ROCK :
9506 SND_BD_AMOEBA_TURNING_TO_GEM));
9509 void AmoebeWaechst(int x, int y)
9511 static unsigned long sound_delay = 0;
9512 static unsigned long sound_delay_value = 0;
9514 if (!MovDelay[x][y]) /* start new growing cycle */
9518 if (DelayReached(&sound_delay, sound_delay_value))
9520 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9521 sound_delay_value = 30;
9525 if (MovDelay[x][y]) /* wait some time before growing bigger */
9528 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9530 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9531 6 - MovDelay[x][y]);
9533 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9536 if (!MovDelay[x][y])
9538 Feld[x][y] = Store[x][y];
9540 TEST_DrawLevelField(x, y);
9545 void AmoebaDisappearing(int x, int y)
9547 static unsigned long sound_delay = 0;
9548 static unsigned long sound_delay_value = 0;
9550 if (!MovDelay[x][y]) /* start new shrinking cycle */
9554 if (DelayReached(&sound_delay, sound_delay_value))
9555 sound_delay_value = 30;
9558 if (MovDelay[x][y]) /* wait some time before shrinking */
9561 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9563 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9564 6 - MovDelay[x][y]);
9566 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9569 if (!MovDelay[x][y])
9571 Feld[x][y] = EL_EMPTY;
9572 TEST_DrawLevelField(x, y);
9574 /* don't let mole enter this field in this cycle;
9575 (give priority to objects falling to this field from above) */
9581 void AmoebeAbleger(int ax, int ay)
9584 int element = Feld[ax][ay];
9585 int graphic = el2img(element);
9586 int newax = ax, neway = ay;
9587 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9588 static int xy[4][2] =
9596 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9598 Feld[ax][ay] = EL_AMOEBA_DEAD;
9599 TEST_DrawLevelField(ax, ay);
9603 if (IS_ANIMATED(graphic))
9604 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9606 if (!MovDelay[ax][ay]) /* start making new amoeba field */
9607 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9609 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
9612 if (MovDelay[ax][ay])
9616 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9619 int x = ax + xy[start][0];
9620 int y = ay + xy[start][1];
9622 if (!IN_LEV_FIELD(x, y))
9625 if (IS_FREE(x, y) ||
9626 CAN_GROW_INTO(Feld[x][y]) ||
9627 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9628 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9634 if (newax == ax && neway == ay)
9637 else /* normal or "filled" (BD style) amoeba */
9640 boolean waiting_for_player = FALSE;
9642 for (i = 0; i < NUM_DIRECTIONS; i++)
9644 int j = (start + i) % 4;
9645 int x = ax + xy[j][0];
9646 int y = ay + xy[j][1];
9648 if (!IN_LEV_FIELD(x, y))
9651 if (IS_FREE(x, y) ||
9652 CAN_GROW_INTO(Feld[x][y]) ||
9653 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9654 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9660 else if (IS_PLAYER(x, y))
9661 waiting_for_player = TRUE;
9664 if (newax == ax && neway == ay) /* amoeba cannot grow */
9666 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9668 Feld[ax][ay] = EL_AMOEBA_DEAD;
9669 TEST_DrawLevelField(ax, ay);
9670 AmoebaCnt[AmoebaNr[ax][ay]]--;
9672 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
9674 if (element == EL_AMOEBA_FULL)
9675 AmoebeUmwandeln(ax, ay);
9676 else if (element == EL_BD_AMOEBA)
9677 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9682 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9684 /* amoeba gets larger by growing in some direction */
9686 int new_group_nr = AmoebaNr[ax][ay];
9689 if (new_group_nr == 0)
9691 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9692 printf("AmoebeAbleger(): This should never happen!\n");
9697 AmoebaNr[newax][neway] = new_group_nr;
9698 AmoebaCnt[new_group_nr]++;
9699 AmoebaCnt2[new_group_nr]++;
9701 /* if amoeba touches other amoeba(s) after growing, unify them */
9702 AmoebenVereinigen(newax, neway);
9704 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9706 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9712 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9713 (neway == lev_fieldy - 1 && newax != ax))
9715 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
9716 Store[newax][neway] = element;
9718 else if (neway == ay || element == EL_EMC_DRIPPER)
9720 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
9722 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9726 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
9727 Feld[ax][ay] = EL_AMOEBA_DROPPING;
9728 Store[ax][ay] = EL_AMOEBA_DROP;
9729 ContinueMoving(ax, ay);
9733 TEST_DrawLevelField(newax, neway);
9736 void Life(int ax, int ay)
9740 int element = Feld[ax][ay];
9741 int graphic = el2img(element);
9742 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9744 boolean changed = FALSE;
9746 if (IS_ANIMATED(graphic))
9747 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9752 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
9753 MovDelay[ax][ay] = life_time;
9755 if (MovDelay[ax][ay]) /* wait some time before next cycle */
9758 if (MovDelay[ax][ay])
9762 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9764 int xx = ax+x1, yy = ay+y1;
9767 if (!IN_LEV_FIELD(xx, yy))
9770 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9772 int x = xx+x2, y = yy+y2;
9774 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9777 if (((Feld[x][y] == element ||
9778 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9780 (IS_FREE(x, y) && Stop[x][y]))
9784 if (xx == ax && yy == ay) /* field in the middle */
9786 if (nachbarn < life_parameter[0] ||
9787 nachbarn > life_parameter[1])
9789 Feld[xx][yy] = EL_EMPTY;
9791 TEST_DrawLevelField(xx, yy);
9792 Stop[xx][yy] = TRUE;
9796 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9797 { /* free border field */
9798 if (nachbarn >= life_parameter[2] &&
9799 nachbarn <= life_parameter[3])
9801 Feld[xx][yy] = element;
9802 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9804 TEST_DrawLevelField(xx, yy);
9805 Stop[xx][yy] = TRUE;
9812 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9813 SND_GAME_OF_LIFE_GROWING);
9816 static void InitRobotWheel(int x, int y)
9818 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9821 static void RunRobotWheel(int x, int y)
9823 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9826 static void StopRobotWheel(int x, int y)
9828 if (ZX == x && ZY == y)
9832 game.robot_wheel_active = FALSE;
9836 static void InitTimegateWheel(int x, int y)
9838 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9841 static void RunTimegateWheel(int x, int y)
9843 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9846 static void InitMagicBallDelay(int x, int y)
9849 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9851 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9855 static void ActivateMagicBall(int bx, int by)
9859 if (level.ball_random)
9861 int pos_border = RND(8); /* select one of the eight border elements */
9862 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9863 int xx = pos_content % 3;
9864 int yy = pos_content / 3;
9869 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9870 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9874 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9876 int xx = x - bx + 1;
9877 int yy = y - by + 1;
9879 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9880 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9884 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9887 void CheckExit(int x, int y)
9889 if (local_player->gems_still_needed > 0 ||
9890 local_player->sokobanfields_still_needed > 0 ||
9891 local_player->lights_still_needed > 0)
9893 int element = Feld[x][y];
9894 int graphic = el2img(element);
9896 if (IS_ANIMATED(graphic))
9897 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9902 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9905 Feld[x][y] = EL_EXIT_OPENING;
9907 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9910 void CheckExitEM(int x, int y)
9912 if (local_player->gems_still_needed > 0 ||
9913 local_player->sokobanfields_still_needed > 0 ||
9914 local_player->lights_still_needed > 0)
9916 int element = Feld[x][y];
9917 int graphic = el2img(element);
9919 if (IS_ANIMATED(graphic))
9920 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9925 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9928 Feld[x][y] = EL_EM_EXIT_OPENING;
9930 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9933 void CheckExitSteel(int x, int y)
9935 if (local_player->gems_still_needed > 0 ||
9936 local_player->sokobanfields_still_needed > 0 ||
9937 local_player->lights_still_needed > 0)
9939 int element = Feld[x][y];
9940 int graphic = el2img(element);
9942 if (IS_ANIMATED(graphic))
9943 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9948 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9951 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9953 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9956 void CheckExitSteelEM(int x, int y)
9958 if (local_player->gems_still_needed > 0 ||
9959 local_player->sokobanfields_still_needed > 0 ||
9960 local_player->lights_still_needed > 0)
9962 int element = Feld[x][y];
9963 int graphic = el2img(element);
9965 if (IS_ANIMATED(graphic))
9966 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9971 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9974 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9976 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9979 void CheckExitSP(int x, int y)
9981 if (local_player->gems_still_needed > 0)
9983 int element = Feld[x][y];
9984 int graphic = el2img(element);
9986 if (IS_ANIMATED(graphic))
9987 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9992 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9995 Feld[x][y] = EL_SP_EXIT_OPENING;
9997 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
10000 static void CloseAllOpenTimegates()
10004 SCAN_PLAYFIELD(x, y)
10006 int element = Feld[x][y];
10008 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
10010 Feld[x][y] = EL_TIMEGATE_CLOSING;
10012 PlayLevelSoundAction(x, y, ACTION_CLOSING);
10017 void DrawTwinkleOnField(int x, int y)
10019 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
10022 if (Feld[x][y] == EL_BD_DIAMOND)
10025 if (MovDelay[x][y] == 0) /* next animation frame */
10026 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
10028 if (MovDelay[x][y] != 0) /* wait some time before next frame */
10032 DrawLevelElementAnimation(x, y, Feld[x][y]);
10034 if (MovDelay[x][y] != 0)
10036 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
10037 10 - MovDelay[x][y]);
10039 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
10044 void MauerWaechst(int x, int y)
10048 if (!MovDelay[x][y]) /* next animation frame */
10049 MovDelay[x][y] = 3 * delay;
10051 if (MovDelay[x][y]) /* wait some time before next frame */
10055 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
10057 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
10058 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
10060 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
10063 if (!MovDelay[x][y])
10065 if (MovDir[x][y] == MV_LEFT)
10067 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
10068 TEST_DrawLevelField(x - 1, y);
10070 else if (MovDir[x][y] == MV_RIGHT)
10072 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
10073 TEST_DrawLevelField(x + 1, y);
10075 else if (MovDir[x][y] == MV_UP)
10077 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
10078 TEST_DrawLevelField(x, y - 1);
10082 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
10083 TEST_DrawLevelField(x, y + 1);
10086 Feld[x][y] = Store[x][y];
10088 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
10089 TEST_DrawLevelField(x, y);
10094 void MauerAbleger(int ax, int ay)
10096 int element = Feld[ax][ay];
10097 int graphic = el2img(element);
10098 boolean oben_frei = FALSE, unten_frei = FALSE;
10099 boolean links_frei = FALSE, rechts_frei = FALSE;
10100 boolean oben_massiv = FALSE, unten_massiv = FALSE;
10101 boolean links_massiv = FALSE, rechts_massiv = FALSE;
10102 boolean new_wall = FALSE;
10104 if (IS_ANIMATED(graphic))
10105 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10107 if (!MovDelay[ax][ay]) /* start building new wall */
10108 MovDelay[ax][ay] = 6;
10110 if (MovDelay[ax][ay]) /* wait some time before building new wall */
10112 MovDelay[ax][ay]--;
10113 if (MovDelay[ax][ay])
10117 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10119 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10121 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10123 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10124 rechts_frei = TRUE;
10126 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
10127 element == EL_EXPANDABLE_WALL_ANY)
10131 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
10132 Store[ax][ay-1] = element;
10133 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10134 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10135 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10136 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
10141 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
10142 Store[ax][ay+1] = element;
10143 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10144 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10145 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10146 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
10151 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10152 element == EL_EXPANDABLE_WALL_ANY ||
10153 element == EL_EXPANDABLE_WALL ||
10154 element == EL_BD_EXPANDABLE_WALL)
10158 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
10159 Store[ax-1][ay] = element;
10160 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10161 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10162 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10163 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
10169 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
10170 Store[ax+1][ay] = element;
10171 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10172 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10173 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10174 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
10179 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
10180 TEST_DrawLevelField(ax, ay);
10182 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10183 oben_massiv = TRUE;
10184 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10185 unten_massiv = TRUE;
10186 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10187 links_massiv = TRUE;
10188 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10189 rechts_massiv = TRUE;
10191 if (((oben_massiv && unten_massiv) ||
10192 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10193 element == EL_EXPANDABLE_WALL) &&
10194 ((links_massiv && rechts_massiv) ||
10195 element == EL_EXPANDABLE_WALL_VERTICAL))
10196 Feld[ax][ay] = EL_WALL;
10199 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10202 void MauerAblegerStahl(int ax, int ay)
10204 int element = Feld[ax][ay];
10205 int graphic = el2img(element);
10206 boolean oben_frei = FALSE, unten_frei = FALSE;
10207 boolean links_frei = FALSE, rechts_frei = FALSE;
10208 boolean oben_massiv = FALSE, unten_massiv = FALSE;
10209 boolean links_massiv = FALSE, rechts_massiv = FALSE;
10210 boolean new_wall = FALSE;
10212 if (IS_ANIMATED(graphic))
10213 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10215 if (!MovDelay[ax][ay]) /* start building new wall */
10216 MovDelay[ax][ay] = 6;
10218 if (MovDelay[ax][ay]) /* wait some time before building new wall */
10220 MovDelay[ax][ay]--;
10221 if (MovDelay[ax][ay])
10225 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10227 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10229 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10231 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10232 rechts_frei = TRUE;
10234 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10235 element == EL_EXPANDABLE_STEELWALL_ANY)
10239 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10240 Store[ax][ay-1] = element;
10241 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10242 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10243 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10244 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10249 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10250 Store[ax][ay+1] = element;
10251 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10252 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10253 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10254 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10259 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10260 element == EL_EXPANDABLE_STEELWALL_ANY)
10264 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10265 Store[ax-1][ay] = element;
10266 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10267 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10268 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10269 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10275 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10276 Store[ax+1][ay] = element;
10277 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10278 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10279 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10280 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10285 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10286 oben_massiv = TRUE;
10287 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10288 unten_massiv = TRUE;
10289 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10290 links_massiv = TRUE;
10291 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10292 rechts_massiv = TRUE;
10294 if (((oben_massiv && unten_massiv) ||
10295 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10296 ((links_massiv && rechts_massiv) ||
10297 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10298 Feld[ax][ay] = EL_STEELWALL;
10301 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10304 void CheckForDragon(int x, int y)
10307 boolean dragon_found = FALSE;
10308 static int xy[4][2] =
10316 for (i = 0; i < NUM_DIRECTIONS; i++)
10318 for (j = 0; j < 4; j++)
10320 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10322 if (IN_LEV_FIELD(xx, yy) &&
10323 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10325 if (Feld[xx][yy] == EL_DRAGON)
10326 dragon_found = TRUE;
10335 for (i = 0; i < NUM_DIRECTIONS; i++)
10337 for (j = 0; j < 3; j++)
10339 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10341 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10343 Feld[xx][yy] = EL_EMPTY;
10344 TEST_DrawLevelField(xx, yy);
10353 static void InitBuggyBase(int x, int y)
10355 int element = Feld[x][y];
10356 int activating_delay = FRAMES_PER_SECOND / 4;
10358 ChangeDelay[x][y] =
10359 (element == EL_SP_BUGGY_BASE ?
10360 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10361 element == EL_SP_BUGGY_BASE_ACTIVATING ?
10363 element == EL_SP_BUGGY_BASE_ACTIVE ?
10364 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10367 static void WarnBuggyBase(int x, int y)
10370 static int xy[4][2] =
10378 for (i = 0; i < NUM_DIRECTIONS; i++)
10380 int xx = x + xy[i][0];
10381 int yy = y + xy[i][1];
10383 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10385 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10392 static void InitTrap(int x, int y)
10394 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10397 static void ActivateTrap(int x, int y)
10399 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10402 static void ChangeActiveTrap(int x, int y)
10404 int graphic = IMG_TRAP_ACTIVE;
10406 /* if new animation frame was drawn, correct crumbled sand border */
10407 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10408 TEST_DrawLevelFieldCrumbled(x, y);
10411 static int getSpecialActionElement(int element, int number, int base_element)
10413 return (element != EL_EMPTY ? element :
10414 number != -1 ? base_element + number - 1 :
10418 static int getModifiedActionNumber(int value_old, int operator, int operand,
10419 int value_min, int value_max)
10421 int value_new = (operator == CA_MODE_SET ? operand :
10422 operator == CA_MODE_ADD ? value_old + operand :
10423 operator == CA_MODE_SUBTRACT ? value_old - operand :
10424 operator == CA_MODE_MULTIPLY ? value_old * operand :
10425 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
10426 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
10429 return (value_new < value_min ? value_min :
10430 value_new > value_max ? value_max :
10434 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10436 struct ElementInfo *ei = &element_info[element];
10437 struct ElementChangeInfo *change = &ei->change_page[page];
10438 int target_element = change->target_element;
10439 int action_type = change->action_type;
10440 int action_mode = change->action_mode;
10441 int action_arg = change->action_arg;
10442 int action_element = change->action_element;
10445 if (!change->has_action)
10448 /* ---------- determine action paramater values -------------------------- */
10450 int level_time_value =
10451 (level.time > 0 ? TimeLeft :
10454 int action_arg_element_raw =
10455 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
10456 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10457 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
10458 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
10459 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10460 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
10461 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
10463 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10466 if (action_arg_element_raw == EL_GROUP_START)
10467 printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10470 int action_arg_direction =
10471 (action_arg >= CA_ARG_DIRECTION_LEFT &&
10472 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10473 action_arg == CA_ARG_DIRECTION_TRIGGER ?
10474 change->actual_trigger_side :
10475 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10476 MV_DIR_OPPOSITE(change->actual_trigger_side) :
10479 int action_arg_number_min =
10480 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10483 int action_arg_number_max =
10484 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10485 action_type == CA_SET_LEVEL_GEMS ? 999 :
10486 action_type == CA_SET_LEVEL_TIME ? 9999 :
10487 action_type == CA_SET_LEVEL_SCORE ? 99999 :
10488 action_type == CA_SET_CE_VALUE ? 9999 :
10489 action_type == CA_SET_CE_SCORE ? 9999 :
10492 int action_arg_number_reset =
10493 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10494 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10495 action_type == CA_SET_LEVEL_TIME ? level.time :
10496 action_type == CA_SET_LEVEL_SCORE ? 0 :
10497 #if USE_NEW_CUSTOM_VALUE
10498 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10500 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10502 action_type == CA_SET_CE_SCORE ? 0 :
10505 int action_arg_number =
10506 (action_arg <= CA_ARG_MAX ? action_arg :
10507 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10508 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10509 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10510 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10511 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10512 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10513 #if USE_NEW_CUSTOM_VALUE
10514 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10516 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10518 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10519 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10520 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10521 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10522 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10523 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10524 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10525 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10526 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10527 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10528 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10529 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
10530 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10531 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
10534 int action_arg_number_old =
10535 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10536 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10537 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10538 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10539 action_type == CA_SET_CE_SCORE ? ei->collect_score :
10542 int action_arg_number_new =
10543 getModifiedActionNumber(action_arg_number_old,
10544 action_mode, action_arg_number,
10545 action_arg_number_min, action_arg_number_max);
10548 int trigger_player_bits =
10549 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10550 change->actual_trigger_player_bits : change->trigger_player);
10552 int trigger_player_bits =
10553 (change->actual_trigger_player >= EL_PLAYER_1 &&
10554 change->actual_trigger_player <= EL_PLAYER_4 ?
10555 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10559 int action_arg_player_bits =
10560 (action_arg >= CA_ARG_PLAYER_1 &&
10561 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10562 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10563 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10566 /* ---------- execute action -------------------------------------------- */
10568 switch (action_type)
10575 /* ---------- level actions ------------------------------------------- */
10577 case CA_RESTART_LEVEL:
10579 game.restart_level = TRUE;
10584 case CA_SHOW_ENVELOPE:
10586 int element = getSpecialActionElement(action_arg_element,
10587 action_arg_number, EL_ENVELOPE_1);
10589 if (IS_ENVELOPE(element))
10590 local_player->show_envelope = element;
10595 case CA_SET_LEVEL_TIME:
10597 if (level.time > 0) /* only modify limited time value */
10599 TimeLeft = action_arg_number_new;
10602 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10604 DisplayGameControlValues();
10606 DrawGameValue_Time(TimeLeft);
10609 if (!TimeLeft && setup.time_limit)
10610 for (i = 0; i < MAX_PLAYERS; i++)
10611 KillPlayer(&stored_player[i]);
10617 case CA_SET_LEVEL_SCORE:
10619 local_player->score = action_arg_number_new;
10622 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10624 DisplayGameControlValues();
10626 DrawGameValue_Score(local_player->score);
10632 case CA_SET_LEVEL_GEMS:
10634 local_player->gems_still_needed = action_arg_number_new;
10637 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10639 DisplayGameControlValues();
10641 DrawGameValue_Emeralds(local_player->gems_still_needed);
10647 #if !USE_PLAYER_GRAVITY
10648 case CA_SET_LEVEL_GRAVITY:
10650 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10651 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10652 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10658 case CA_SET_LEVEL_WIND:
10660 game.wind_direction = action_arg_direction;
10665 case CA_SET_LEVEL_RANDOM_SEED:
10668 /* ensure that setting a new random seed while playing is predictable */
10669 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10671 InitRND(action_arg_number_new);
10675 printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10683 for (i = 0; i < 9; i++)
10684 printf("%d, ", RND(2));
10692 /* ---------- player actions ------------------------------------------ */
10694 case CA_MOVE_PLAYER:
10696 /* automatically move to the next field in specified direction */
10697 for (i = 0; i < MAX_PLAYERS; i++)
10698 if (trigger_player_bits & (1 << i))
10699 stored_player[i].programmed_action = action_arg_direction;
10704 case CA_EXIT_PLAYER:
10706 for (i = 0; i < MAX_PLAYERS; i++)
10707 if (action_arg_player_bits & (1 << i))
10708 PlayerWins(&stored_player[i]);
10713 case CA_KILL_PLAYER:
10715 for (i = 0; i < MAX_PLAYERS; i++)
10716 if (action_arg_player_bits & (1 << i))
10717 KillPlayer(&stored_player[i]);
10722 case CA_SET_PLAYER_KEYS:
10724 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10725 int element = getSpecialActionElement(action_arg_element,
10726 action_arg_number, EL_KEY_1);
10728 if (IS_KEY(element))
10730 for (i = 0; i < MAX_PLAYERS; i++)
10732 if (trigger_player_bits & (1 << i))
10734 stored_player[i].key[KEY_NR(element)] = key_state;
10736 DrawGameDoorValues();
10744 case CA_SET_PLAYER_SPEED:
10747 printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10750 for (i = 0; i < MAX_PLAYERS; i++)
10752 if (trigger_player_bits & (1 << i))
10754 int move_stepsize = TILEX / stored_player[i].move_delay_value;
10756 if (action_arg == CA_ARG_SPEED_FASTER &&
10757 stored_player[i].cannot_move)
10759 action_arg_number = STEPSIZE_VERY_SLOW;
10761 else if (action_arg == CA_ARG_SPEED_SLOWER ||
10762 action_arg == CA_ARG_SPEED_FASTER)
10764 action_arg_number = 2;
10765 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10768 else if (action_arg == CA_ARG_NUMBER_RESET)
10770 action_arg_number = level.initial_player_stepsize[i];
10774 getModifiedActionNumber(move_stepsize,
10777 action_arg_number_min,
10778 action_arg_number_max);
10780 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10787 case CA_SET_PLAYER_SHIELD:
10789 for (i = 0; i < MAX_PLAYERS; i++)
10791 if (trigger_player_bits & (1 << i))
10793 if (action_arg == CA_ARG_SHIELD_OFF)
10795 stored_player[i].shield_normal_time_left = 0;
10796 stored_player[i].shield_deadly_time_left = 0;
10798 else if (action_arg == CA_ARG_SHIELD_NORMAL)
10800 stored_player[i].shield_normal_time_left = 999999;
10802 else if (action_arg == CA_ARG_SHIELD_DEADLY)
10804 stored_player[i].shield_normal_time_left = 999999;
10805 stored_player[i].shield_deadly_time_left = 999999;
10813 #if USE_PLAYER_GRAVITY
10814 case CA_SET_PLAYER_GRAVITY:
10816 for (i = 0; i < MAX_PLAYERS; i++)
10818 if (trigger_player_bits & (1 << i))
10820 stored_player[i].gravity =
10821 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10822 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10823 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10824 stored_player[i].gravity);
10832 case CA_SET_PLAYER_ARTWORK:
10834 for (i = 0; i < MAX_PLAYERS; i++)
10836 if (trigger_player_bits & (1 << i))
10838 int artwork_element = action_arg_element;
10840 if (action_arg == CA_ARG_ELEMENT_RESET)
10842 (level.use_artwork_element[i] ? level.artwork_element[i] :
10843 stored_player[i].element_nr);
10845 #if USE_GFX_RESET_PLAYER_ARTWORK
10846 if (stored_player[i].artwork_element != artwork_element)
10847 stored_player[i].Frame = 0;
10850 stored_player[i].artwork_element = artwork_element;
10852 SetPlayerWaiting(&stored_player[i], FALSE);
10854 /* set number of special actions for bored and sleeping animation */
10855 stored_player[i].num_special_action_bored =
10856 get_num_special_action(artwork_element,
10857 ACTION_BORING_1, ACTION_BORING_LAST);
10858 stored_player[i].num_special_action_sleeping =
10859 get_num_special_action(artwork_element,
10860 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10867 case CA_SET_PLAYER_INVENTORY:
10869 for (i = 0; i < MAX_PLAYERS; i++)
10871 struct PlayerInfo *player = &stored_player[i];
10874 if (trigger_player_bits & (1 << i))
10876 int inventory_element = action_arg_element;
10878 if (action_arg == CA_ARG_ELEMENT_TARGET ||
10879 action_arg == CA_ARG_ELEMENT_TRIGGER ||
10880 action_arg == CA_ARG_ELEMENT_ACTION)
10882 int element = inventory_element;
10883 int collect_count = element_info[element].collect_count_initial;
10885 if (!IS_CUSTOM_ELEMENT(element))
10888 if (collect_count == 0)
10889 player->inventory_infinite_element = element;
10891 for (k = 0; k < collect_count; k++)
10892 if (player->inventory_size < MAX_INVENTORY_SIZE)
10893 player->inventory_element[player->inventory_size++] =
10896 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10897 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10898 action_arg == CA_ARG_INVENTORY_RM_ACTION)
10900 if (player->inventory_infinite_element != EL_UNDEFINED &&
10901 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10902 action_arg_element_raw))
10903 player->inventory_infinite_element = EL_UNDEFINED;
10905 for (k = 0, j = 0; j < player->inventory_size; j++)
10907 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10908 action_arg_element_raw))
10909 player->inventory_element[k++] = player->inventory_element[j];
10912 player->inventory_size = k;
10914 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10916 if (player->inventory_size > 0)
10918 for (j = 0; j < player->inventory_size - 1; j++)
10919 player->inventory_element[j] = player->inventory_element[j + 1];
10921 player->inventory_size--;
10924 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10926 if (player->inventory_size > 0)
10927 player->inventory_size--;
10929 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10931 player->inventory_infinite_element = EL_UNDEFINED;
10932 player->inventory_size = 0;
10934 else if (action_arg == CA_ARG_INVENTORY_RESET)
10936 player->inventory_infinite_element = EL_UNDEFINED;
10937 player->inventory_size = 0;
10939 if (level.use_initial_inventory[i])
10941 for (j = 0; j < level.initial_inventory_size[i]; j++)
10943 int element = level.initial_inventory_content[i][j];
10944 int collect_count = element_info[element].collect_count_initial;
10946 if (!IS_CUSTOM_ELEMENT(element))
10949 if (collect_count == 0)
10950 player->inventory_infinite_element = element;
10952 for (k = 0; k < collect_count; k++)
10953 if (player->inventory_size < MAX_INVENTORY_SIZE)
10954 player->inventory_element[player->inventory_size++] =
10965 /* ---------- CE actions ---------------------------------------------- */
10967 case CA_SET_CE_VALUE:
10969 #if USE_NEW_CUSTOM_VALUE
10970 int last_ce_value = CustomValue[x][y];
10972 CustomValue[x][y] = action_arg_number_new;
10974 if (CustomValue[x][y] != last_ce_value)
10976 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10977 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10979 if (CustomValue[x][y] == 0)
10981 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10982 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10990 case CA_SET_CE_SCORE:
10992 #if USE_NEW_CUSTOM_VALUE
10993 int last_ce_score = ei->collect_score;
10995 ei->collect_score = action_arg_number_new;
10997 if (ei->collect_score != last_ce_score)
10999 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
11000 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
11002 if (ei->collect_score == 0)
11006 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
11007 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
11010 This is a very special case that seems to be a mixture between
11011 CheckElementChange() and CheckTriggeredElementChange(): while
11012 the first one only affects single elements that are triggered
11013 directly, the second one affects multiple elements in the playfield
11014 that are triggered indirectly by another element. This is a third
11015 case: Changing the CE score always affects multiple identical CEs,
11016 so every affected CE must be checked, not only the single CE for
11017 which the CE score was changed in the first place (as every instance
11018 of that CE shares the same CE score, and therefore also can change)!
11020 SCAN_PLAYFIELD(xx, yy)
11022 if (Feld[xx][yy] == element)
11023 CheckElementChange(xx, yy, element, EL_UNDEFINED,
11024 CE_SCORE_GETS_ZERO);
11033 case CA_SET_CE_ARTWORK:
11035 int artwork_element = action_arg_element;
11036 boolean reset_frame = FALSE;
11039 if (action_arg == CA_ARG_ELEMENT_RESET)
11040 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
11043 if (ei->gfx_element != artwork_element)
11044 reset_frame = TRUE;
11046 ei->gfx_element = artwork_element;
11048 SCAN_PLAYFIELD(xx, yy)
11050 if (Feld[xx][yy] == element)
11054 ResetGfxAnimation(xx, yy);
11055 ResetRandomAnimationValue(xx, yy);
11058 TEST_DrawLevelField(xx, yy);
11065 /* ---------- engine actions ------------------------------------------ */
11067 case CA_SET_ENGINE_SCAN_MODE:
11069 InitPlayfieldScanMode(action_arg);
11079 static void CreateFieldExt(int x, int y, int element, boolean is_change)
11081 int old_element = Feld[x][y];
11082 int new_element = GetElementFromGroupElement(element);
11083 int previous_move_direction = MovDir[x][y];
11084 #if USE_NEW_CUSTOM_VALUE
11085 int last_ce_value = CustomValue[x][y];
11087 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
11088 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
11089 boolean add_player_onto_element = (new_element_is_player &&
11090 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
11091 /* this breaks SnakeBite when a snake is
11092 halfway through a door that closes */
11093 /* NOW FIXED AT LEVEL INIT IN files.c */
11094 new_element != EL_SOKOBAN_FIELD_PLAYER &&
11096 IS_WALKABLE(old_element));
11099 /* check if element under the player changes from accessible to unaccessible
11100 (needed for special case of dropping element which then changes) */
11101 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11102 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11110 if (!add_player_onto_element)
11112 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
11113 RemoveMovingField(x, y);
11117 Feld[x][y] = new_element;
11119 #if !USE_GFX_RESET_GFX_ANIMATION
11120 ResetGfxAnimation(x, y);
11121 ResetRandomAnimationValue(x, y);
11124 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
11125 MovDir[x][y] = previous_move_direction;
11127 #if USE_NEW_CUSTOM_VALUE
11128 if (element_info[new_element].use_last_ce_value)
11129 CustomValue[x][y] = last_ce_value;
11132 InitField_WithBug1(x, y, FALSE);
11134 new_element = Feld[x][y]; /* element may have changed */
11136 #if USE_GFX_RESET_GFX_ANIMATION
11137 ResetGfxAnimation(x, y);
11138 ResetRandomAnimationValue(x, y);
11141 TEST_DrawLevelField(x, y);
11143 if (GFX_CRUMBLED(new_element))
11144 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
11148 /* check if element under the player changes from accessible to unaccessible
11149 (needed for special case of dropping element which then changes) */
11150 /* (must be checked after creating new element for walkable group elements) */
11151 #if USE_FIX_KILLED_BY_NON_WALKABLE
11152 if (IS_PLAYER(x, y) && !player_explosion_protected &&
11153 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11160 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11161 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11170 /* "ChangeCount" not set yet to allow "entered by player" change one time */
11171 if (new_element_is_player)
11172 RelocatePlayer(x, y, new_element);
11175 ChangeCount[x][y]++; /* count number of changes in the same frame */
11177 TestIfBadThingTouchesPlayer(x, y);
11178 TestIfPlayerTouchesCustomElement(x, y);
11179 TestIfElementTouchesCustomElement(x, y);
11182 static void CreateField(int x, int y, int element)
11184 CreateFieldExt(x, y, element, FALSE);
11187 static void CreateElementFromChange(int x, int y, int element)
11189 element = GET_VALID_RUNTIME_ELEMENT(element);
11191 #if USE_STOP_CHANGED_ELEMENTS
11192 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11194 int old_element = Feld[x][y];
11196 /* prevent changed element from moving in same engine frame
11197 unless both old and new element can either fall or move */
11198 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
11199 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
11204 CreateFieldExt(x, y, element, TRUE);
11207 static boolean ChangeElement(int x, int y, int element, int page)
11209 struct ElementInfo *ei = &element_info[element];
11210 struct ElementChangeInfo *change = &ei->change_page[page];
11211 int ce_value = CustomValue[x][y];
11212 int ce_score = ei->collect_score;
11213 int target_element;
11214 int old_element = Feld[x][y];
11216 /* always use default change event to prevent running into a loop */
11217 if (ChangeEvent[x][y] == -1)
11218 ChangeEvent[x][y] = CE_DELAY;
11220 if (ChangeEvent[x][y] == CE_DELAY)
11222 /* reset actual trigger element, trigger player and action element */
11223 change->actual_trigger_element = EL_EMPTY;
11224 change->actual_trigger_player = EL_EMPTY;
11225 change->actual_trigger_player_bits = CH_PLAYER_NONE;
11226 change->actual_trigger_side = CH_SIDE_NONE;
11227 change->actual_trigger_ce_value = 0;
11228 change->actual_trigger_ce_score = 0;
11231 /* do not change elements more than a specified maximum number of changes */
11232 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11235 ChangeCount[x][y]++; /* count number of changes in the same frame */
11237 if (change->explode)
11244 if (change->use_target_content)
11246 boolean complete_replace = TRUE;
11247 boolean can_replace[3][3];
11250 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11253 boolean is_walkable;
11254 boolean is_diggable;
11255 boolean is_collectible;
11256 boolean is_removable;
11257 boolean is_destructible;
11258 int ex = x + xx - 1;
11259 int ey = y + yy - 1;
11260 int content_element = change->target_content.e[xx][yy];
11263 can_replace[xx][yy] = TRUE;
11265 if (ex == x && ey == y) /* do not check changing element itself */
11268 if (content_element == EL_EMPTY_SPACE)
11270 can_replace[xx][yy] = FALSE; /* do not replace border with space */
11275 if (!IN_LEV_FIELD(ex, ey))
11277 can_replace[xx][yy] = FALSE;
11278 complete_replace = FALSE;
11285 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11286 e = MovingOrBlocked2Element(ex, ey);
11288 is_empty = (IS_FREE(ex, ey) ||
11289 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11291 is_walkable = (is_empty || IS_WALKABLE(e));
11292 is_diggable = (is_empty || IS_DIGGABLE(e));
11293 is_collectible = (is_empty || IS_COLLECTIBLE(e));
11294 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11295 is_removable = (is_diggable || is_collectible);
11297 can_replace[xx][yy] =
11298 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
11299 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
11300 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
11301 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
11302 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
11303 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11304 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11306 if (!can_replace[xx][yy])
11307 complete_replace = FALSE;
11310 if (!change->only_if_complete || complete_replace)
11312 boolean something_has_changed = FALSE;
11314 if (change->only_if_complete && change->use_random_replace &&
11315 RND(100) < change->random_percentage)
11318 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11320 int ex = x + xx - 1;
11321 int ey = y + yy - 1;
11322 int content_element;
11324 if (can_replace[xx][yy] && (!change->use_random_replace ||
11325 RND(100) < change->random_percentage))
11327 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11328 RemoveMovingField(ex, ey);
11330 ChangeEvent[ex][ey] = ChangeEvent[x][y];
11332 content_element = change->target_content.e[xx][yy];
11333 target_element = GET_TARGET_ELEMENT(element, content_element, change,
11334 ce_value, ce_score);
11336 CreateElementFromChange(ex, ey, target_element);
11338 something_has_changed = TRUE;
11340 /* for symmetry reasons, freeze newly created border elements */
11341 if (ex != x || ey != y)
11342 Stop[ex][ey] = TRUE; /* no more moving in this frame */
11346 if (something_has_changed)
11348 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11349 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11355 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11356 ce_value, ce_score);
11358 if (element == EL_DIAGONAL_GROWING ||
11359 element == EL_DIAGONAL_SHRINKING)
11361 target_element = Store[x][y];
11363 Store[x][y] = EL_EMPTY;
11366 CreateElementFromChange(x, y, target_element);
11368 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11369 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11372 /* this uses direct change before indirect change */
11373 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11378 #if USE_NEW_DELAYED_ACTION
11380 static void HandleElementChange(int x, int y, int page)
11382 int element = MovingOrBlocked2Element(x, y);
11383 struct ElementInfo *ei = &element_info[element];
11384 struct ElementChangeInfo *change = &ei->change_page[page];
11385 boolean handle_action_before_change = FALSE;
11388 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11389 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11392 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11393 x, y, element, element_info[element].token_name);
11394 printf("HandleElementChange(): This should never happen!\n");
11399 /* this can happen with classic bombs on walkable, changing elements */
11400 if (!CAN_CHANGE_OR_HAS_ACTION(element))
11403 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11404 ChangeDelay[x][y] = 0;
11410 if (ChangeDelay[x][y] == 0) /* initialize element change */
11412 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11414 if (change->can_change)
11417 /* !!! not clear why graphic animation should be reset at all here !!! */
11418 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11419 #if USE_GFX_RESET_WHEN_NOT_MOVING
11420 /* when a custom element is about to change (for example by change delay),
11421 do not reset graphic animation when the custom element is moving */
11422 if (!IS_MOVING(x, y))
11425 ResetGfxAnimation(x, y);
11426 ResetRandomAnimationValue(x, y);
11430 if (change->pre_change_function)
11431 change->pre_change_function(x, y);
11435 ChangeDelay[x][y]--;
11437 if (ChangeDelay[x][y] != 0) /* continue element change */
11439 if (change->can_change)
11441 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11443 if (IS_ANIMATED(graphic))
11444 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11446 if (change->change_function)
11447 change->change_function(x, y);
11450 else /* finish element change */
11452 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11454 page = ChangePage[x][y];
11455 ChangePage[x][y] = -1;
11457 change = &ei->change_page[page];
11460 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11462 ChangeDelay[x][y] = 1; /* try change after next move step */
11463 ChangePage[x][y] = page; /* remember page to use for change */
11469 /* special case: set new level random seed before changing element */
11470 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11471 handle_action_before_change = TRUE;
11473 if (change->has_action && handle_action_before_change)
11474 ExecuteCustomElementAction(x, y, element, page);
11477 if (change->can_change)
11479 if (ChangeElement(x, y, element, page))
11481 if (change->post_change_function)
11482 change->post_change_function(x, y);
11486 if (change->has_action && !handle_action_before_change)
11487 ExecuteCustomElementAction(x, y, element, page);
11493 static void HandleElementChange(int x, int y, int page)
11495 int element = MovingOrBlocked2Element(x, y);
11496 struct ElementInfo *ei = &element_info[element];
11497 struct ElementChangeInfo *change = &ei->change_page[page];
11500 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11503 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11504 x, y, element, element_info[element].token_name);
11505 printf("HandleElementChange(): This should never happen!\n");
11510 /* this can happen with classic bombs on walkable, changing elements */
11511 if (!CAN_CHANGE(element))
11514 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11515 ChangeDelay[x][y] = 0;
11521 if (ChangeDelay[x][y] == 0) /* initialize element change */
11523 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11525 ResetGfxAnimation(x, y);
11526 ResetRandomAnimationValue(x, y);
11528 if (change->pre_change_function)
11529 change->pre_change_function(x, y);
11532 ChangeDelay[x][y]--;
11534 if (ChangeDelay[x][y] != 0) /* continue element change */
11536 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11538 if (IS_ANIMATED(graphic))
11539 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11541 if (change->change_function)
11542 change->change_function(x, y);
11544 else /* finish element change */
11546 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11548 page = ChangePage[x][y];
11549 ChangePage[x][y] = -1;
11551 change = &ei->change_page[page];
11554 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11556 ChangeDelay[x][y] = 1; /* try change after next move step */
11557 ChangePage[x][y] = page; /* remember page to use for change */
11562 if (ChangeElement(x, y, element, page))
11564 if (change->post_change_function)
11565 change->post_change_function(x, y);
11572 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11573 int trigger_element,
11575 int trigger_player,
11579 boolean change_done_any = FALSE;
11580 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11583 if (!(trigger_events[trigger_element][trigger_event]))
11587 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11588 trigger_event, recursion_loop_depth, recursion_loop_detected,
11589 recursion_loop_element, EL_NAME(recursion_loop_element));
11592 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11594 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11596 int element = EL_CUSTOM_START + i;
11597 boolean change_done = FALSE;
11600 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11601 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11604 for (p = 0; p < element_info[element].num_change_pages; p++)
11606 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11608 if (change->can_change_or_has_action &&
11609 change->has_event[trigger_event] &&
11610 change->trigger_side & trigger_side &&
11611 change->trigger_player & trigger_player &&
11612 change->trigger_page & trigger_page_bits &&
11613 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11615 change->actual_trigger_element = trigger_element;
11616 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11617 change->actual_trigger_player_bits = trigger_player;
11618 change->actual_trigger_side = trigger_side;
11619 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11620 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11623 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11624 element, EL_NAME(element), p);
11627 if ((change->can_change && !change_done) || change->has_action)
11631 SCAN_PLAYFIELD(x, y)
11633 if (Feld[x][y] == element)
11635 if (change->can_change && !change_done)
11637 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11638 /* if element already changed in this frame, not only prevent
11639 another element change (checked in ChangeElement()), but
11640 also prevent additional element actions for this element */
11642 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11643 !level.use_action_after_change_bug)
11648 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11649 element, EL_NAME(element), p);
11652 ChangeDelay[x][y] = 1;
11653 ChangeEvent[x][y] = trigger_event;
11655 HandleElementChange(x, y, p);
11657 #if USE_NEW_DELAYED_ACTION
11658 else if (change->has_action)
11660 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11661 /* if element already changed in this frame, not only prevent
11662 another element change (checked in ChangeElement()), but
11663 also prevent additional element actions for this element */
11665 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11666 !level.use_action_after_change_bug)
11672 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11673 element, EL_NAME(element), p);
11676 ExecuteCustomElementAction(x, y, element, p);
11677 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11680 if (change->has_action)
11682 ExecuteCustomElementAction(x, y, element, p);
11683 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11689 if (change->can_change)
11691 change_done = TRUE;
11692 change_done_any = TRUE;
11695 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11696 element, EL_NAME(element), p);
11705 RECURSION_LOOP_DETECTION_END();
11707 return change_done_any;
11710 static boolean CheckElementChangeExt(int x, int y,
11712 int trigger_element,
11714 int trigger_player,
11717 boolean change_done = FALSE;
11720 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11721 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11724 if (Feld[x][y] == EL_BLOCKED)
11726 Blocked2Moving(x, y, &x, &y);
11727 element = Feld[x][y];
11731 /* check if element has already changed */
11732 if (Feld[x][y] != element)
11735 /* check if element has already changed or is about to change after moving */
11736 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11737 Feld[x][y] != element) ||
11739 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11740 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11741 ChangePage[x][y] != -1)))
11746 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11747 trigger_event, recursion_loop_depth, recursion_loop_detected,
11748 recursion_loop_element, EL_NAME(recursion_loop_element));
11751 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11754 printf("::: X: trigger_player_bits == %d\n", trigger_player);
11757 for (p = 0; p < element_info[element].num_change_pages; p++)
11759 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11761 /* check trigger element for all events where the element that is checked
11762 for changing interacts with a directly adjacent element -- this is
11763 different to element changes that affect other elements to change on the
11764 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11765 boolean check_trigger_element =
11766 (trigger_event == CE_TOUCHING_X ||
11767 trigger_event == CE_HITTING_X ||
11768 trigger_event == CE_HIT_BY_X ||
11770 /* this one was forgotten until 3.2.3 */
11771 trigger_event == CE_DIGGING_X);
11774 if (change->can_change_or_has_action &&
11775 change->has_event[trigger_event] &&
11776 change->trigger_side & trigger_side &&
11777 change->trigger_player & trigger_player &&
11778 (!check_trigger_element ||
11779 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11781 change->actual_trigger_element = trigger_element;
11782 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11783 change->actual_trigger_player_bits = trigger_player;
11784 change->actual_trigger_side = trigger_side;
11785 change->actual_trigger_ce_value = CustomValue[x][y];
11786 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11788 /* special case: trigger element not at (x,y) position for some events */
11789 if (check_trigger_element)
11801 { 0, 0 }, { 0, 0 }, { 0, 0 },
11805 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11806 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11808 change->actual_trigger_ce_value = CustomValue[xx][yy];
11809 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11812 if (change->can_change && !change_done)
11814 ChangeDelay[x][y] = 1;
11815 ChangeEvent[x][y] = trigger_event;
11817 HandleElementChange(x, y, p);
11819 change_done = TRUE;
11821 #if USE_NEW_DELAYED_ACTION
11822 else if (change->has_action)
11824 ExecuteCustomElementAction(x, y, element, p);
11825 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11828 if (change->has_action)
11830 ExecuteCustomElementAction(x, y, element, p);
11831 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11837 RECURSION_LOOP_DETECTION_END();
11839 return change_done;
11842 static void PlayPlayerSound(struct PlayerInfo *player)
11844 int jx = player->jx, jy = player->jy;
11845 int sound_element = player->artwork_element;
11846 int last_action = player->last_action_waiting;
11847 int action = player->action_waiting;
11849 if (player->is_waiting)
11851 if (action != last_action)
11852 PlayLevelSoundElementAction(jx, jy, sound_element, action);
11854 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11858 if (action != last_action)
11859 StopSound(element_info[sound_element].sound[last_action]);
11861 if (last_action == ACTION_SLEEPING)
11862 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11866 static void PlayAllPlayersSound()
11870 for (i = 0; i < MAX_PLAYERS; i++)
11871 if (stored_player[i].active)
11872 PlayPlayerSound(&stored_player[i]);
11875 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11877 boolean last_waiting = player->is_waiting;
11878 int move_dir = player->MovDir;
11880 player->dir_waiting = move_dir;
11881 player->last_action_waiting = player->action_waiting;
11885 if (!last_waiting) /* not waiting -> waiting */
11887 player->is_waiting = TRUE;
11889 player->frame_counter_bored =
11891 game.player_boring_delay_fixed +
11892 GetSimpleRandom(game.player_boring_delay_random);
11893 player->frame_counter_sleeping =
11895 game.player_sleeping_delay_fixed +
11896 GetSimpleRandom(game.player_sleeping_delay_random);
11898 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11901 if (game.player_sleeping_delay_fixed +
11902 game.player_sleeping_delay_random > 0 &&
11903 player->anim_delay_counter == 0 &&
11904 player->post_delay_counter == 0 &&
11905 FrameCounter >= player->frame_counter_sleeping)
11906 player->is_sleeping = TRUE;
11907 else if (game.player_boring_delay_fixed +
11908 game.player_boring_delay_random > 0 &&
11909 FrameCounter >= player->frame_counter_bored)
11910 player->is_bored = TRUE;
11912 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11913 player->is_bored ? ACTION_BORING :
11916 if (player->is_sleeping && player->use_murphy)
11918 /* special case for sleeping Murphy when leaning against non-free tile */
11920 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11921 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11922 !IS_MOVING(player->jx - 1, player->jy)))
11923 move_dir = MV_LEFT;
11924 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11925 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11926 !IS_MOVING(player->jx + 1, player->jy)))
11927 move_dir = MV_RIGHT;
11929 player->is_sleeping = FALSE;
11931 player->dir_waiting = move_dir;
11934 if (player->is_sleeping)
11936 if (player->num_special_action_sleeping > 0)
11938 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11940 int last_special_action = player->special_action_sleeping;
11941 int num_special_action = player->num_special_action_sleeping;
11942 int special_action =
11943 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11944 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11945 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11946 last_special_action + 1 : ACTION_SLEEPING);
11947 int special_graphic =
11948 el_act_dir2img(player->artwork_element, special_action, move_dir);
11950 player->anim_delay_counter =
11951 graphic_info[special_graphic].anim_delay_fixed +
11952 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11953 player->post_delay_counter =
11954 graphic_info[special_graphic].post_delay_fixed +
11955 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11957 player->special_action_sleeping = special_action;
11960 if (player->anim_delay_counter > 0)
11962 player->action_waiting = player->special_action_sleeping;
11963 player->anim_delay_counter--;
11965 else if (player->post_delay_counter > 0)
11967 player->post_delay_counter--;
11971 else if (player->is_bored)
11973 if (player->num_special_action_bored > 0)
11975 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11977 int special_action =
11978 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11979 int special_graphic =
11980 el_act_dir2img(player->artwork_element, special_action, move_dir);
11982 player->anim_delay_counter =
11983 graphic_info[special_graphic].anim_delay_fixed +
11984 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11985 player->post_delay_counter =
11986 graphic_info[special_graphic].post_delay_fixed +
11987 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11989 player->special_action_bored = special_action;
11992 if (player->anim_delay_counter > 0)
11994 player->action_waiting = player->special_action_bored;
11995 player->anim_delay_counter--;
11997 else if (player->post_delay_counter > 0)
11999 player->post_delay_counter--;
12004 else if (last_waiting) /* waiting -> not waiting */
12006 player->is_waiting = FALSE;
12007 player->is_bored = FALSE;
12008 player->is_sleeping = FALSE;
12010 player->frame_counter_bored = -1;
12011 player->frame_counter_sleeping = -1;
12013 player->anim_delay_counter = 0;
12014 player->post_delay_counter = 0;
12016 player->dir_waiting = player->MovDir;
12017 player->action_waiting = ACTION_DEFAULT;
12019 player->special_action_bored = ACTION_DEFAULT;
12020 player->special_action_sleeping = ACTION_DEFAULT;
12024 static void CheckSingleStepMode(struct PlayerInfo *player)
12026 if (tape.single_step && tape.recording && !tape.pausing)
12028 /* as it is called "single step mode", just return to pause mode when the
12029 player stopped moving after one tile (or never starts moving at all) */
12030 if (!player->is_moving && !player->is_pushing)
12032 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12033 SnapField(player, 0, 0); /* stop snapping */
12038 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
12040 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
12041 int left = player_action & JOY_LEFT;
12042 int right = player_action & JOY_RIGHT;
12043 int up = player_action & JOY_UP;
12044 int down = player_action & JOY_DOWN;
12045 int button1 = player_action & JOY_BUTTON_1;
12046 int button2 = player_action & JOY_BUTTON_2;
12047 int dx = (left ? -1 : right ? 1 : 0);
12048 int dy = (up ? -1 : down ? 1 : 0);
12050 if (!player->active || tape.pausing)
12056 snapped = SnapField(player, dx, dy);
12060 dropped = DropElement(player);
12062 moved = MovePlayer(player, dx, dy);
12065 CheckSingleStepMode(player);
12067 SetPlayerWaiting(player, FALSE);
12069 return player_action;
12073 /* no actions for this player (no input at player's configured device) */
12075 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
12076 SnapField(player, 0, 0);
12077 CheckGravityMovementWhenNotMoving(player);
12079 if (player->MovPos == 0)
12080 SetPlayerWaiting(player, TRUE);
12082 if (player->MovPos == 0) /* needed for tape.playing */
12083 player->is_moving = FALSE;
12085 player->is_dropping = FALSE;
12086 player->is_dropping_pressed = FALSE;
12087 player->drop_pressed_delay = 0;
12089 CheckSingleStepMode(player);
12095 static void CheckLevelTime()
12099 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
12100 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12102 if (level.native_em_level->lev->home == 0) /* all players at home */
12104 PlayerWins(local_player);
12106 AllPlayersGone = TRUE;
12108 level.native_em_level->lev->home = -1;
12111 if (level.native_em_level->ply[0]->alive == 0 &&
12112 level.native_em_level->ply[1]->alive == 0 &&
12113 level.native_em_level->ply[2]->alive == 0 &&
12114 level.native_em_level->ply[3]->alive == 0) /* all dead */
12115 AllPlayersGone = TRUE;
12117 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12119 if (game_sp.LevelSolved &&
12120 !game_sp.GameOver) /* game won */
12122 PlayerWins(local_player);
12124 game_sp.GameOver = TRUE;
12126 AllPlayersGone = TRUE;
12129 if (game_sp.GameOver) /* game lost */
12130 AllPlayersGone = TRUE;
12133 if (TimeFrames >= FRAMES_PER_SECOND)
12138 for (i = 0; i < MAX_PLAYERS; i++)
12140 struct PlayerInfo *player = &stored_player[i];
12142 if (SHIELD_ON(player))
12144 player->shield_normal_time_left--;
12146 if (player->shield_deadly_time_left > 0)
12147 player->shield_deadly_time_left--;
12151 if (!local_player->LevelSolved && !level.use_step_counter)
12159 if (TimeLeft <= 10 && setup.time_limit)
12160 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12163 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12165 DisplayGameControlValues();
12167 DrawGameValue_Time(TimeLeft);
12170 if (!TimeLeft && setup.time_limit)
12172 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12173 level.native_em_level->lev->killed_out_of_time = TRUE;
12175 for (i = 0; i < MAX_PLAYERS; i++)
12176 KillPlayer(&stored_player[i]);
12180 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12182 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12184 DisplayGameControlValues();
12187 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12188 DrawGameValue_Time(TimePlayed);
12191 level.native_em_level->lev->time =
12192 (level.time == 0 ? TimePlayed : TimeLeft);
12195 if (tape.recording || tape.playing)
12196 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12200 UpdateAndDisplayGameControlValues();
12202 UpdateGameDoorValues();
12203 DrawGameDoorValues();
12207 void AdvanceFrameAndPlayerCounters(int player_nr)
12211 /* advance frame counters (global frame counter and time frame counter) */
12215 /* advance player counters (counters for move delay, move animation etc.) */
12216 for (i = 0; i < MAX_PLAYERS; i++)
12218 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12219 int move_delay_value = stored_player[i].move_delay_value;
12220 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12222 if (!advance_player_counters) /* not all players may be affected */
12225 #if USE_NEW_PLAYER_ANIM
12226 if (move_frames == 0) /* less than one move per game frame */
12228 int stepsize = TILEX / move_delay_value;
12229 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12230 int count = (stored_player[i].is_moving ?
12231 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12233 if (count % delay == 0)
12238 stored_player[i].Frame += move_frames;
12240 if (stored_player[i].MovPos != 0)
12241 stored_player[i].StepFrame += move_frames;
12243 if (stored_player[i].move_delay > 0)
12244 stored_player[i].move_delay--;
12246 /* due to bugs in previous versions, counter must count up, not down */
12247 if (stored_player[i].push_delay != -1)
12248 stored_player[i].push_delay++;
12250 if (stored_player[i].drop_delay > 0)
12251 stored_player[i].drop_delay--;
12253 if (stored_player[i].is_dropping_pressed)
12254 stored_player[i].drop_pressed_delay++;
12258 void StartGameActions(boolean init_network_game, boolean record_tape,
12261 unsigned long new_random_seed = InitRND(random_seed);
12264 TapeStartRecording(new_random_seed);
12266 #if defined(NETWORK_AVALIABLE)
12267 if (init_network_game)
12269 SendToServer_StartPlaying();
12280 static unsigned long game_frame_delay = 0;
12281 unsigned long game_frame_delay_value;
12282 byte *recorded_player_action;
12283 byte summarized_player_action = 0;
12284 byte tape_action[MAX_PLAYERS];
12287 /* detect endless loops, caused by custom element programming */
12288 if (recursion_loop_detected && recursion_loop_depth == 0)
12290 char *message = getStringCat3("Internal Error ! Element ",
12291 EL_NAME(recursion_loop_element),
12292 " caused endless loop ! Quit the game ?");
12294 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12295 EL_NAME(recursion_loop_element));
12297 RequestQuitGameExt(FALSE, level_editor_test_game, message);
12299 recursion_loop_detected = FALSE; /* if game should be continued */
12306 if (game.restart_level)
12307 StartGameActions(options.network, setup.autorecord, level.random_seed);
12309 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
12310 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12312 if (level.native_em_level->lev->home == 0) /* all players at home */
12314 PlayerWins(local_player);
12316 AllPlayersGone = TRUE;
12318 level.native_em_level->lev->home = -1;
12321 if (level.native_em_level->ply[0]->alive == 0 &&
12322 level.native_em_level->ply[1]->alive == 0 &&
12323 level.native_em_level->ply[2]->alive == 0 &&
12324 level.native_em_level->ply[3]->alive == 0) /* all dead */
12325 AllPlayersGone = TRUE;
12327 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12329 if (game_sp.LevelSolved &&
12330 !game_sp.GameOver) /* game won */
12332 PlayerWins(local_player);
12334 game_sp.GameOver = TRUE;
12336 AllPlayersGone = TRUE;
12339 if (game_sp.GameOver) /* game lost */
12340 AllPlayersGone = TRUE;
12343 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12346 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12349 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
12352 game_frame_delay_value =
12353 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12355 if (tape.playing && tape.warp_forward && !tape.pausing)
12356 game_frame_delay_value = 0;
12358 /* ---------- main game synchronization point ---------- */
12360 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12362 if (network_playing && !network_player_action_received)
12364 /* try to get network player actions in time */
12366 #if defined(NETWORK_AVALIABLE)
12367 /* last chance to get network player actions without main loop delay */
12368 HandleNetworking();
12371 /* game was quit by network peer */
12372 if (game_status != GAME_MODE_PLAYING)
12375 if (!network_player_action_received)
12376 return; /* failed to get network player actions in time */
12378 /* do not yet reset "network_player_action_received" (for tape.pausing) */
12384 /* at this point we know that we really continue executing the game */
12386 network_player_action_received = FALSE;
12388 /* when playing tape, read previously recorded player input from tape data */
12389 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12392 /* TapePlayAction() may return NULL when toggling to "pause before death" */
12397 if (tape.set_centered_player)
12399 game.centered_player_nr_next = tape.centered_player_nr_next;
12400 game.set_centered_player = TRUE;
12403 for (i = 0; i < MAX_PLAYERS; i++)
12405 summarized_player_action |= stored_player[i].action;
12407 if (!network_playing)
12408 stored_player[i].effective_action = stored_player[i].action;
12411 #if defined(NETWORK_AVALIABLE)
12412 if (network_playing)
12413 SendToServer_MovePlayer(summarized_player_action);
12416 if (!options.network && !setup.team_mode)
12417 local_player->effective_action = summarized_player_action;
12419 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
12421 for (i = 0; i < MAX_PLAYERS; i++)
12422 stored_player[i].effective_action =
12423 (i == game.centered_player_nr ? summarized_player_action : 0);
12426 if (recorded_player_action != NULL)
12427 for (i = 0; i < MAX_PLAYERS; i++)
12428 stored_player[i].effective_action = recorded_player_action[i];
12430 for (i = 0; i < MAX_PLAYERS; i++)
12432 tape_action[i] = stored_player[i].effective_action;
12434 /* (this can only happen in the R'n'D game engine) */
12435 if (tape.recording && tape_action[i] && !tape.player_participates[i])
12436 tape.player_participates[i] = TRUE; /* player just appeared from CE */
12439 /* only record actions from input devices, but not programmed actions */
12440 if (tape.recording)
12441 TapeRecordAction(tape_action);
12443 #if USE_NEW_PLAYER_ASSIGNMENTS
12445 byte mapped_action[MAX_PLAYERS];
12447 for (i = 0; i < MAX_PLAYERS; i++)
12448 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12450 for (i = 0; i < MAX_PLAYERS; i++)
12451 stored_player[i].effective_action = mapped_action[i];
12455 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12457 GameActions_EM_Main();
12459 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12461 GameActions_SP_Main();
12469 void GameActions_EM_Main()
12471 byte effective_action[MAX_PLAYERS];
12472 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12475 for (i = 0; i < MAX_PLAYERS; i++)
12476 effective_action[i] = stored_player[i].effective_action;
12478 GameActions_EM(effective_action, warp_mode);
12482 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12485 void GameActions_SP_Main()
12487 byte effective_action[MAX_PLAYERS];
12488 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12491 for (i = 0; i < MAX_PLAYERS; i++)
12492 effective_action[i] = stored_player[i].effective_action;
12494 GameActions_SP(effective_action, warp_mode);
12498 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12501 void GameActions_RND()
12503 int magic_wall_x = 0, magic_wall_y = 0;
12504 int i, x, y, element, graphic;
12506 InitPlayfieldScanModeVars();
12508 #if USE_ONE_MORE_CHANGE_PER_FRAME
12509 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12511 SCAN_PLAYFIELD(x, y)
12513 ChangeCount[x][y] = 0;
12514 ChangeEvent[x][y] = -1;
12519 if (game.set_centered_player)
12521 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12523 /* switching to "all players" only possible if all players fit to screen */
12524 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12526 game.centered_player_nr_next = game.centered_player_nr;
12527 game.set_centered_player = FALSE;
12530 /* do not switch focus to non-existing (or non-active) player */
12531 if (game.centered_player_nr_next >= 0 &&
12532 !stored_player[game.centered_player_nr_next].active)
12534 game.centered_player_nr_next = game.centered_player_nr;
12535 game.set_centered_player = FALSE;
12539 if (game.set_centered_player &&
12540 ScreenMovPos == 0) /* screen currently aligned at tile position */
12544 if (game.centered_player_nr_next == -1)
12546 setScreenCenteredToAllPlayers(&sx, &sy);
12550 sx = stored_player[game.centered_player_nr_next].jx;
12551 sy = stored_player[game.centered_player_nr_next].jy;
12554 game.centered_player_nr = game.centered_player_nr_next;
12555 game.set_centered_player = FALSE;
12557 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12558 DrawGameDoorValues();
12561 for (i = 0; i < MAX_PLAYERS; i++)
12563 int actual_player_action = stored_player[i].effective_action;
12566 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12567 - rnd_equinox_tetrachloride 048
12568 - rnd_equinox_tetrachloride_ii 096
12569 - rnd_emanuel_schmieg 002
12570 - doctor_sloan_ww 001, 020
12572 if (stored_player[i].MovPos == 0)
12573 CheckGravityMovement(&stored_player[i]);
12576 /* overwrite programmed action with tape action */
12577 if (stored_player[i].programmed_action)
12578 actual_player_action = stored_player[i].programmed_action;
12580 PlayerActions(&stored_player[i], actual_player_action);
12582 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12585 ScrollScreen(NULL, SCROLL_GO_ON);
12587 /* for backwards compatibility, the following code emulates a fixed bug that
12588 occured when pushing elements (causing elements that just made their last
12589 pushing step to already (if possible) make their first falling step in the
12590 same game frame, which is bad); this code is also needed to use the famous
12591 "spring push bug" which is used in older levels and might be wanted to be
12592 used also in newer levels, but in this case the buggy pushing code is only
12593 affecting the "spring" element and no other elements */
12595 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12597 for (i = 0; i < MAX_PLAYERS; i++)
12599 struct PlayerInfo *player = &stored_player[i];
12600 int x = player->jx;
12601 int y = player->jy;
12603 if (player->active && player->is_pushing && player->is_moving &&
12605 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12606 Feld[x][y] == EL_SPRING))
12608 ContinueMoving(x, y);
12610 /* continue moving after pushing (this is actually a bug) */
12611 if (!IS_MOVING(x, y))
12612 Stop[x][y] = FALSE;
12618 debug_print_timestamp(0, "start main loop profiling");
12621 SCAN_PLAYFIELD(x, y)
12623 ChangeCount[x][y] = 0;
12624 ChangeEvent[x][y] = -1;
12626 /* this must be handled before main playfield loop */
12627 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12630 if (MovDelay[x][y] <= 0)
12634 #if USE_NEW_SNAP_DELAY
12635 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12638 if (MovDelay[x][y] <= 0)
12641 TEST_DrawLevelField(x, y);
12643 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12649 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12651 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12652 printf("GameActions(): This should never happen!\n");
12654 ChangePage[x][y] = -1;
12658 Stop[x][y] = FALSE;
12659 if (WasJustMoving[x][y] > 0)
12660 WasJustMoving[x][y]--;
12661 if (WasJustFalling[x][y] > 0)
12662 WasJustFalling[x][y]--;
12663 if (CheckCollision[x][y] > 0)
12664 CheckCollision[x][y]--;
12665 if (CheckImpact[x][y] > 0)
12666 CheckImpact[x][y]--;
12670 /* reset finished pushing action (not done in ContinueMoving() to allow
12671 continuous pushing animation for elements with zero push delay) */
12672 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12674 ResetGfxAnimation(x, y);
12675 TEST_DrawLevelField(x, y);
12679 if (IS_BLOCKED(x, y))
12683 Blocked2Moving(x, y, &oldx, &oldy);
12684 if (!IS_MOVING(oldx, oldy))
12686 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12687 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12688 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12689 printf("GameActions(): This should never happen!\n");
12696 debug_print_timestamp(0, "- time for pre-main loop:");
12699 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
12700 SCAN_PLAYFIELD(x, y)
12702 element = Feld[x][y];
12703 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12708 int element2 = element;
12709 int graphic2 = graphic;
12711 int element2 = Feld[x][y];
12712 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12714 int last_gfx_frame = GfxFrame[x][y];
12716 if (graphic_info[graphic2].anim_global_sync)
12717 GfxFrame[x][y] = FrameCounter;
12718 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12719 GfxFrame[x][y] = CustomValue[x][y];
12720 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12721 GfxFrame[x][y] = element_info[element2].collect_score;
12722 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12723 GfxFrame[x][y] = ChangeDelay[x][y];
12725 if (redraw && GfxFrame[x][y] != last_gfx_frame)
12726 DrawLevelGraphicAnimation(x, y, graphic2);
12729 ResetGfxFrame(x, y, TRUE);
12733 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12734 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12735 ResetRandomAnimationValue(x, y);
12739 SetRandomAnimationValue(x, y);
12743 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12746 #endif // -------------------- !!! TEST ONLY !!! --------------------
12749 debug_print_timestamp(0, "- time for TEST loop: -->");
12752 SCAN_PLAYFIELD(x, y)
12754 element = Feld[x][y];
12755 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12757 ResetGfxFrame(x, y, TRUE);
12759 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12760 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12761 ResetRandomAnimationValue(x, y);
12763 SetRandomAnimationValue(x, y);
12765 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12767 if (IS_INACTIVE(element))
12769 if (IS_ANIMATED(graphic))
12770 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12775 /* this may take place after moving, so 'element' may have changed */
12776 if (IS_CHANGING(x, y) &&
12777 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12779 int page = element_info[element].event_page_nr[CE_DELAY];
12782 HandleElementChange(x, y, page);
12784 if (CAN_CHANGE(element))
12785 HandleElementChange(x, y, page);
12787 if (HAS_ACTION(element))
12788 ExecuteCustomElementAction(x, y, element, page);
12791 element = Feld[x][y];
12792 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12795 #if 0 // ---------------------------------------------------------------------
12797 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12801 element = Feld[x][y];
12802 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12804 if (IS_ANIMATED(graphic) &&
12805 !IS_MOVING(x, y) &&
12807 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12809 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12810 TEST_DrawTwinkleOnField(x, y);
12812 else if (IS_MOVING(x, y))
12813 ContinueMoving(x, y);
12820 case EL_EM_EXIT_OPEN:
12821 case EL_SP_EXIT_OPEN:
12822 case EL_STEEL_EXIT_OPEN:
12823 case EL_EM_STEEL_EXIT_OPEN:
12824 case EL_SP_TERMINAL:
12825 case EL_SP_TERMINAL_ACTIVE:
12826 case EL_EXTRA_TIME:
12827 case EL_SHIELD_NORMAL:
12828 case EL_SHIELD_DEADLY:
12829 if (IS_ANIMATED(graphic))
12830 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12833 case EL_DYNAMITE_ACTIVE:
12834 case EL_EM_DYNAMITE_ACTIVE:
12835 case EL_DYNABOMB_PLAYER_1_ACTIVE:
12836 case EL_DYNABOMB_PLAYER_2_ACTIVE:
12837 case EL_DYNABOMB_PLAYER_3_ACTIVE:
12838 case EL_DYNABOMB_PLAYER_4_ACTIVE:
12839 case EL_SP_DISK_RED_ACTIVE:
12840 CheckDynamite(x, y);
12843 case EL_AMOEBA_GROWING:
12844 AmoebeWaechst(x, y);
12847 case EL_AMOEBA_SHRINKING:
12848 AmoebaDisappearing(x, y);
12851 #if !USE_NEW_AMOEBA_CODE
12852 case EL_AMOEBA_WET:
12853 case EL_AMOEBA_DRY:
12854 case EL_AMOEBA_FULL:
12856 case EL_EMC_DRIPPER:
12857 AmoebeAbleger(x, y);
12861 case EL_GAME_OF_LIFE:
12866 case EL_EXIT_CLOSED:
12870 case EL_EM_EXIT_CLOSED:
12874 case EL_STEEL_EXIT_CLOSED:
12875 CheckExitSteel(x, y);
12878 case EL_EM_STEEL_EXIT_CLOSED:
12879 CheckExitSteelEM(x, y);
12882 case EL_SP_EXIT_CLOSED:
12886 case EL_EXPANDABLE_WALL_GROWING:
12887 case EL_EXPANDABLE_STEELWALL_GROWING:
12888 MauerWaechst(x, y);
12891 case EL_EXPANDABLE_WALL:
12892 case EL_EXPANDABLE_WALL_HORIZONTAL:
12893 case EL_EXPANDABLE_WALL_VERTICAL:
12894 case EL_EXPANDABLE_WALL_ANY:
12895 case EL_BD_EXPANDABLE_WALL:
12896 MauerAbleger(x, y);
12899 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12900 case EL_EXPANDABLE_STEELWALL_VERTICAL:
12901 case EL_EXPANDABLE_STEELWALL_ANY:
12902 MauerAblegerStahl(x, y);
12906 CheckForDragon(x, y);
12912 case EL_ELEMENT_SNAPPING:
12913 case EL_DIAGONAL_SHRINKING:
12914 case EL_DIAGONAL_GROWING:
12917 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12919 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12924 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12925 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12930 #else // ---------------------------------------------------------------------
12932 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12936 element = Feld[x][y];
12937 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12939 if (IS_ANIMATED(graphic) &&
12940 !IS_MOVING(x, y) &&
12942 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12944 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12945 TEST_DrawTwinkleOnField(x, y);
12947 else if ((element == EL_ACID ||
12948 element == EL_EXIT_OPEN ||
12949 element == EL_EM_EXIT_OPEN ||
12950 element == EL_SP_EXIT_OPEN ||
12951 element == EL_STEEL_EXIT_OPEN ||
12952 element == EL_EM_STEEL_EXIT_OPEN ||
12953 element == EL_SP_TERMINAL ||
12954 element == EL_SP_TERMINAL_ACTIVE ||
12955 element == EL_EXTRA_TIME ||
12956 element == EL_SHIELD_NORMAL ||
12957 element == EL_SHIELD_DEADLY) &&
12958 IS_ANIMATED(graphic))
12959 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12960 else if (IS_MOVING(x, y))
12961 ContinueMoving(x, y);
12962 else if (IS_ACTIVE_BOMB(element))
12963 CheckDynamite(x, y);
12964 else if (element == EL_AMOEBA_GROWING)
12965 AmoebeWaechst(x, y);
12966 else if (element == EL_AMOEBA_SHRINKING)
12967 AmoebaDisappearing(x, y);
12969 #if !USE_NEW_AMOEBA_CODE
12970 else if (IS_AMOEBALIVE(element))
12971 AmoebeAbleger(x, y);
12974 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12976 else if (element == EL_EXIT_CLOSED)
12978 else if (element == EL_EM_EXIT_CLOSED)
12980 else if (element == EL_STEEL_EXIT_CLOSED)
12981 CheckExitSteel(x, y);
12982 else if (element == EL_EM_STEEL_EXIT_CLOSED)
12983 CheckExitSteelEM(x, y);
12984 else if (element == EL_SP_EXIT_CLOSED)
12986 else if (element == EL_EXPANDABLE_WALL_GROWING ||
12987 element == EL_EXPANDABLE_STEELWALL_GROWING)
12988 MauerWaechst(x, y);
12989 else if (element == EL_EXPANDABLE_WALL ||
12990 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12991 element == EL_EXPANDABLE_WALL_VERTICAL ||
12992 element == EL_EXPANDABLE_WALL_ANY ||
12993 element == EL_BD_EXPANDABLE_WALL)
12994 MauerAbleger(x, y);
12995 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12996 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12997 element == EL_EXPANDABLE_STEELWALL_ANY)
12998 MauerAblegerStahl(x, y);
12999 else if (element == EL_FLAMES)
13000 CheckForDragon(x, y);
13001 else if (element == EL_EXPLOSION)
13002 ; /* drawing of correct explosion animation is handled separately */
13003 else if (element == EL_ELEMENT_SNAPPING ||
13004 element == EL_DIAGONAL_SHRINKING ||
13005 element == EL_DIAGONAL_GROWING)
13007 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
13009 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13011 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
13012 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13014 #endif // ---------------------------------------------------------------------
13016 if (IS_BELT_ACTIVE(element))
13017 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
13019 if (game.magic_wall_active)
13021 int jx = local_player->jx, jy = local_player->jy;
13023 /* play the element sound at the position nearest to the player */
13024 if ((element == EL_MAGIC_WALL_FULL ||
13025 element == EL_MAGIC_WALL_ACTIVE ||
13026 element == EL_MAGIC_WALL_EMPTYING ||
13027 element == EL_BD_MAGIC_WALL_FULL ||
13028 element == EL_BD_MAGIC_WALL_ACTIVE ||
13029 element == EL_BD_MAGIC_WALL_EMPTYING ||
13030 element == EL_DC_MAGIC_WALL_FULL ||
13031 element == EL_DC_MAGIC_WALL_ACTIVE ||
13032 element == EL_DC_MAGIC_WALL_EMPTYING) &&
13033 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
13042 debug_print_timestamp(0, "- time for MAIN loop: -->");
13045 #if USE_NEW_AMOEBA_CODE
13046 /* new experimental amoeba growth stuff */
13047 if (!(FrameCounter % 8))
13049 static unsigned long random = 1684108901;
13051 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
13053 x = RND(lev_fieldx);
13054 y = RND(lev_fieldy);
13055 element = Feld[x][y];
13057 if (!IS_PLAYER(x,y) &&
13058 (element == EL_EMPTY ||
13059 CAN_GROW_INTO(element) ||
13060 element == EL_QUICKSAND_EMPTY ||
13061 element == EL_QUICKSAND_FAST_EMPTY ||
13062 element == EL_ACID_SPLASH_LEFT ||
13063 element == EL_ACID_SPLASH_RIGHT))
13065 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
13066 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
13067 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
13068 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
13069 Feld[x][y] = EL_AMOEBA_DROP;
13072 random = random * 129 + 1;
13078 if (game.explosions_delayed)
13081 game.explosions_delayed = FALSE;
13083 SCAN_PLAYFIELD(x, y)
13085 element = Feld[x][y];
13087 if (ExplodeField[x][y])
13088 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
13089 else if (element == EL_EXPLOSION)
13090 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
13092 ExplodeField[x][y] = EX_TYPE_NONE;
13095 game.explosions_delayed = TRUE;
13098 if (game.magic_wall_active)
13100 if (!(game.magic_wall_time_left % 4))
13102 int element = Feld[magic_wall_x][magic_wall_y];
13104 if (element == EL_BD_MAGIC_WALL_FULL ||
13105 element == EL_BD_MAGIC_WALL_ACTIVE ||
13106 element == EL_BD_MAGIC_WALL_EMPTYING)
13107 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
13108 else if (element == EL_DC_MAGIC_WALL_FULL ||
13109 element == EL_DC_MAGIC_WALL_ACTIVE ||
13110 element == EL_DC_MAGIC_WALL_EMPTYING)
13111 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
13113 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
13116 if (game.magic_wall_time_left > 0)
13118 game.magic_wall_time_left--;
13120 if (!game.magic_wall_time_left)
13122 SCAN_PLAYFIELD(x, y)
13124 element = Feld[x][y];
13126 if (element == EL_MAGIC_WALL_ACTIVE ||
13127 element == EL_MAGIC_WALL_FULL)
13129 Feld[x][y] = EL_MAGIC_WALL_DEAD;
13130 TEST_DrawLevelField(x, y);
13132 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
13133 element == EL_BD_MAGIC_WALL_FULL)
13135 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
13136 TEST_DrawLevelField(x, y);
13138 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
13139 element == EL_DC_MAGIC_WALL_FULL)
13141 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
13142 TEST_DrawLevelField(x, y);
13146 game.magic_wall_active = FALSE;
13151 if (game.light_time_left > 0)
13153 game.light_time_left--;
13155 if (game.light_time_left == 0)
13156 RedrawAllLightSwitchesAndInvisibleElements();
13159 if (game.timegate_time_left > 0)
13161 game.timegate_time_left--;
13163 if (game.timegate_time_left == 0)
13164 CloseAllOpenTimegates();
13167 if (game.lenses_time_left > 0)
13169 game.lenses_time_left--;
13171 if (game.lenses_time_left == 0)
13172 RedrawAllInvisibleElementsForLenses();
13175 if (game.magnify_time_left > 0)
13177 game.magnify_time_left--;
13179 if (game.magnify_time_left == 0)
13180 RedrawAllInvisibleElementsForMagnifier();
13183 for (i = 0; i < MAX_PLAYERS; i++)
13185 struct PlayerInfo *player = &stored_player[i];
13187 if (SHIELD_ON(player))
13189 if (player->shield_deadly_time_left)
13190 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13191 else if (player->shield_normal_time_left)
13192 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13196 #if USE_DELAYED_GFX_REDRAW
13197 SCAN_PLAYFIELD(x, y)
13200 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13202 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13203 GfxRedraw[x][y] != GFX_REDRAW_NONE)
13206 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13207 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13209 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13210 DrawLevelField(x, y);
13212 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13213 DrawLevelFieldCrumbled(x, y);
13215 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13216 DrawLevelFieldCrumbledNeighbours(x, y);
13218 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13219 DrawTwinkleOnField(x, y);
13222 GfxRedraw[x][y] = GFX_REDRAW_NONE;
13229 PlayAllPlayersSound();
13231 if (options.debug) /* calculate frames per second */
13233 static unsigned long fps_counter = 0;
13234 static int fps_frames = 0;
13235 unsigned long fps_delay_ms = Counter() - fps_counter;
13239 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
13241 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13244 fps_counter = Counter();
13247 redraw_mask |= REDRAW_FPS;
13250 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
13252 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13254 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13256 local_player->show_envelope = 0;
13260 debug_print_timestamp(0, "stop main loop profiling ");
13261 printf("----------------------------------------------------------\n");
13264 /* use random number generator in every frame to make it less predictable */
13265 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13269 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13271 int min_x = x, min_y = y, max_x = x, max_y = y;
13274 for (i = 0; i < MAX_PLAYERS; i++)
13276 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13278 if (!stored_player[i].active || &stored_player[i] == player)
13281 min_x = MIN(min_x, jx);
13282 min_y = MIN(min_y, jy);
13283 max_x = MAX(max_x, jx);
13284 max_y = MAX(max_y, jy);
13287 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13290 static boolean AllPlayersInVisibleScreen()
13294 for (i = 0; i < MAX_PLAYERS; i++)
13296 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13298 if (!stored_player[i].active)
13301 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13308 void ScrollLevel(int dx, int dy)
13311 /* (directly solved in BlitBitmap() now) */
13312 static Bitmap *bitmap_db_field2 = NULL;
13313 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13320 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13321 /* only horizontal XOR vertical scroll direction allowed */
13322 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13327 /* (directly solved in BlitBitmap() now) */
13328 if (bitmap_db_field2 == NULL)
13329 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13331 /* needed when blitting directly to same bitmap -- should not be needed with
13332 recent SDL libraries, but apparently does not work in 1.2.11 directly */
13333 BlitBitmap(drawto_field, bitmap_db_field2,
13334 FX + TILEX * (dx == -1) - softscroll_offset,
13335 FY + TILEY * (dy == -1) - softscroll_offset,
13336 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13337 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13338 FX + TILEX * (dx == 1) - softscroll_offset,
13339 FY + TILEY * (dy == 1) - softscroll_offset);
13340 BlitBitmap(bitmap_db_field2, drawto_field,
13341 FX + TILEX * (dx == 1) - softscroll_offset,
13342 FY + TILEY * (dy == 1) - softscroll_offset,
13343 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13344 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13345 FX + TILEX * (dx == 1) - softscroll_offset,
13346 FY + TILEY * (dy == 1) - softscroll_offset);
13351 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13352 int xsize = (BX2 - BX1 + 1);
13353 int ysize = (BY2 - BY1 + 1);
13354 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13355 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13356 int step = (start < end ? +1 : -1);
13358 for (i = start; i != end; i += step)
13360 BlitBitmap(drawto_field, drawto_field,
13361 FX + TILEX * (dx != 0 ? i + step : 0),
13362 FY + TILEY * (dy != 0 ? i + step : 0),
13363 TILEX * (dx != 0 ? 1 : xsize),
13364 TILEY * (dy != 0 ? 1 : ysize),
13365 FX + TILEX * (dx != 0 ? i : 0),
13366 FY + TILEY * (dy != 0 ? i : 0));
13372 int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX : 0);
13374 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13377 BlitBitmap(drawto_field, drawto_field,
13378 FX + TILEX * (dx == -1) - softscroll_offset,
13379 FY + TILEY * (dy == -1) - softscroll_offset,
13380 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13381 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13382 FX + TILEX * (dx == 1) - softscroll_offset,
13383 FY + TILEY * (dy == 1) - softscroll_offset);
13389 x = (dx == 1 ? BX1 : BX2);
13390 for (y = BY1; y <= BY2; y++)
13391 DrawScreenField(x, y);
13396 y = (dy == 1 ? BY1 : BY2);
13397 for (x = BX1; x <= BX2; x++)
13398 DrawScreenField(x, y);
13401 redraw_mask |= REDRAW_FIELD;
13404 static boolean canFallDown(struct PlayerInfo *player)
13406 int jx = player->jx, jy = player->jy;
13408 return (IN_LEV_FIELD(jx, jy + 1) &&
13409 (IS_FREE(jx, jy + 1) ||
13410 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13411 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13412 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13415 static boolean canPassField(int x, int y, int move_dir)
13417 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13418 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13419 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13420 int nextx = x + dx;
13421 int nexty = y + dy;
13422 int element = Feld[x][y];
13424 return (IS_PASSABLE_FROM(element, opposite_dir) &&
13425 !CAN_MOVE(element) &&
13426 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13427 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13428 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13431 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13433 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13434 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13435 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13439 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13440 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13441 (IS_DIGGABLE(Feld[newx][newy]) ||
13442 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13443 canPassField(newx, newy, move_dir)));
13446 static void CheckGravityMovement(struct PlayerInfo *player)
13448 #if USE_PLAYER_GRAVITY
13449 if (player->gravity && !player->programmed_action)
13451 if (game.gravity && !player->programmed_action)
13454 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13455 int move_dir_vertical = player->effective_action & MV_VERTICAL;
13456 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13457 int jx = player->jx, jy = player->jy;
13458 boolean player_is_moving_to_valid_field =
13459 (!player_is_snapping &&
13460 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13461 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13462 boolean player_can_fall_down = canFallDown(player);
13464 if (player_can_fall_down &&
13465 !player_is_moving_to_valid_field)
13466 player->programmed_action = MV_DOWN;
13470 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13472 return CheckGravityMovement(player);
13474 #if USE_PLAYER_GRAVITY
13475 if (player->gravity && !player->programmed_action)
13477 if (game.gravity && !player->programmed_action)
13480 int jx = player->jx, jy = player->jy;
13481 boolean field_under_player_is_free =
13482 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13483 boolean player_is_standing_on_valid_field =
13484 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13485 (IS_WALKABLE(Feld[jx][jy]) &&
13486 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13488 if (field_under_player_is_free && !player_is_standing_on_valid_field)
13489 player->programmed_action = MV_DOWN;
13494 MovePlayerOneStep()
13495 -----------------------------------------------------------------------------
13496 dx, dy: direction (non-diagonal) to try to move the player to
13497 real_dx, real_dy: direction as read from input device (can be diagonal)
13500 boolean MovePlayerOneStep(struct PlayerInfo *player,
13501 int dx, int dy, int real_dx, int real_dy)
13503 int jx = player->jx, jy = player->jy;
13504 int new_jx = jx + dx, new_jy = jy + dy;
13505 #if !USE_FIXED_DONT_RUN_INTO
13509 boolean player_can_move = !player->cannot_move;
13511 if (!player->active || (!dx && !dy))
13512 return MP_NO_ACTION;
13514 player->MovDir = (dx < 0 ? MV_LEFT :
13515 dx > 0 ? MV_RIGHT :
13517 dy > 0 ? MV_DOWN : MV_NONE);
13519 if (!IN_LEV_FIELD(new_jx, new_jy))
13520 return MP_NO_ACTION;
13522 if (!player_can_move)
13524 if (player->MovPos == 0)
13526 player->is_moving = FALSE;
13527 player->is_digging = FALSE;
13528 player->is_collecting = FALSE;
13529 player->is_snapping = FALSE;
13530 player->is_pushing = FALSE;
13535 if (!options.network && game.centered_player_nr == -1 &&
13536 !AllPlayersInSight(player, new_jx, new_jy))
13537 return MP_NO_ACTION;
13539 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13540 return MP_NO_ACTION;
13543 #if !USE_FIXED_DONT_RUN_INTO
13544 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13546 /* (moved to DigField()) */
13547 if (player_can_move && DONT_RUN_INTO(element))
13549 if (element == EL_ACID && dx == 0 && dy == 1)
13551 SplashAcid(new_jx, new_jy);
13552 Feld[jx][jy] = EL_PLAYER_1;
13553 InitMovingField(jx, jy, MV_DOWN);
13554 Store[jx][jy] = EL_ACID;
13555 ContinueMoving(jx, jy);
13556 BuryPlayer(player);
13559 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13565 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13566 if (can_move != MP_MOVING)
13569 /* check if DigField() has caused relocation of the player */
13570 if (player->jx != jx || player->jy != jy)
13571 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13573 StorePlayer[jx][jy] = 0;
13574 player->last_jx = jx;
13575 player->last_jy = jy;
13576 player->jx = new_jx;
13577 player->jy = new_jy;
13578 StorePlayer[new_jx][new_jy] = player->element_nr;
13580 if (player->move_delay_value_next != -1)
13582 player->move_delay_value = player->move_delay_value_next;
13583 player->move_delay_value_next = -1;
13587 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13589 player->step_counter++;
13591 PlayerVisit[jx][jy] = FrameCounter;
13593 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13594 player->is_moving = TRUE;
13598 /* should better be called in MovePlayer(), but this breaks some tapes */
13599 ScrollPlayer(player, SCROLL_INIT);
13605 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13607 int jx = player->jx, jy = player->jy;
13608 int old_jx = jx, old_jy = jy;
13609 int moved = MP_NO_ACTION;
13611 if (!player->active)
13616 if (player->MovPos == 0)
13618 player->is_moving = FALSE;
13619 player->is_digging = FALSE;
13620 player->is_collecting = FALSE;
13621 player->is_snapping = FALSE;
13622 player->is_pushing = FALSE;
13628 if (player->move_delay > 0)
13631 player->move_delay = -1; /* set to "uninitialized" value */
13633 /* store if player is automatically moved to next field */
13634 player->is_auto_moving = (player->programmed_action != MV_NONE);
13636 /* remove the last programmed player action */
13637 player->programmed_action = 0;
13639 if (player->MovPos)
13641 /* should only happen if pre-1.2 tape recordings are played */
13642 /* this is only for backward compatibility */
13644 int original_move_delay_value = player->move_delay_value;
13647 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
13651 /* scroll remaining steps with finest movement resolution */
13652 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13654 while (player->MovPos)
13656 ScrollPlayer(player, SCROLL_GO_ON);
13657 ScrollScreen(NULL, SCROLL_GO_ON);
13659 AdvanceFrameAndPlayerCounters(player->index_nr);
13665 player->move_delay_value = original_move_delay_value;
13668 player->is_active = FALSE;
13670 if (player->last_move_dir & MV_HORIZONTAL)
13672 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13673 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13677 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13678 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13681 #if USE_FIXED_BORDER_RUNNING_GFX
13682 if (!moved && !player->is_active)
13684 player->is_moving = FALSE;
13685 player->is_digging = FALSE;
13686 player->is_collecting = FALSE;
13687 player->is_snapping = FALSE;
13688 player->is_pushing = FALSE;
13696 if (moved & MP_MOVING && !ScreenMovPos &&
13697 (player->index_nr == game.centered_player_nr ||
13698 game.centered_player_nr == -1))
13700 if (moved & MP_MOVING && !ScreenMovPos &&
13701 (player == local_player || !options.network))
13704 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13705 int offset = game.scroll_delay_value;
13707 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13709 /* actual player has left the screen -- scroll in that direction */
13710 if (jx != old_jx) /* player has moved horizontally */
13711 scroll_x += (jx - old_jx);
13712 else /* player has moved vertically */
13713 scroll_y += (jy - old_jy);
13717 if (jx != old_jx) /* player has moved horizontally */
13719 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
13720 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13721 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13723 /* don't scroll over playfield boundaries */
13724 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13725 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13727 /* don't scroll more than one field at a time */
13728 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13730 /* don't scroll against the player's moving direction */
13731 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
13732 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13733 scroll_x = old_scroll_x;
13735 else /* player has moved vertically */
13737 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
13738 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13739 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13741 /* don't scroll over playfield boundaries */
13742 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13743 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13745 /* don't scroll more than one field at a time */
13746 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13748 /* don't scroll against the player's moving direction */
13749 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
13750 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13751 scroll_y = old_scroll_y;
13755 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13758 if (!options.network && game.centered_player_nr == -1 &&
13759 !AllPlayersInVisibleScreen())
13761 scroll_x = old_scroll_x;
13762 scroll_y = old_scroll_y;
13766 if (!options.network && !AllPlayersInVisibleScreen())
13768 scroll_x = old_scroll_x;
13769 scroll_y = old_scroll_y;
13774 ScrollScreen(player, SCROLL_INIT);
13775 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13780 player->StepFrame = 0;
13782 if (moved & MP_MOVING)
13784 if (old_jx != jx && old_jy == jy)
13785 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13786 else if (old_jx == jx && old_jy != jy)
13787 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13789 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
13791 player->last_move_dir = player->MovDir;
13792 player->is_moving = TRUE;
13793 player->is_snapping = FALSE;
13794 player->is_switching = FALSE;
13795 player->is_dropping = FALSE;
13796 player->is_dropping_pressed = FALSE;
13797 player->drop_pressed_delay = 0;
13800 /* should better be called here than above, but this breaks some tapes */
13801 ScrollPlayer(player, SCROLL_INIT);
13806 CheckGravityMovementWhenNotMoving(player);
13808 player->is_moving = FALSE;
13810 /* at this point, the player is allowed to move, but cannot move right now
13811 (e.g. because of something blocking the way) -- ensure that the player
13812 is also allowed to move in the next frame (in old versions before 3.1.1,
13813 the player was forced to wait again for eight frames before next try) */
13815 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13816 player->move_delay = 0; /* allow direct movement in the next frame */
13819 if (player->move_delay == -1) /* not yet initialized by DigField() */
13820 player->move_delay = player->move_delay_value;
13822 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13824 TestIfPlayerTouchesBadThing(jx, jy);
13825 TestIfPlayerTouchesCustomElement(jx, jy);
13828 if (!player->active)
13829 RemovePlayer(player);
13834 void ScrollPlayer(struct PlayerInfo *player, int mode)
13836 int jx = player->jx, jy = player->jy;
13837 int last_jx = player->last_jx, last_jy = player->last_jy;
13838 int move_stepsize = TILEX / player->move_delay_value;
13840 #if USE_NEW_PLAYER_SPEED
13841 if (!player->active)
13844 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
13847 if (!player->active || player->MovPos == 0)
13851 if (mode == SCROLL_INIT)
13853 player->actual_frame_counter = FrameCounter;
13854 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13856 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13857 Feld[last_jx][last_jy] == EL_EMPTY)
13859 int last_field_block_delay = 0; /* start with no blocking at all */
13860 int block_delay_adjustment = player->block_delay_adjustment;
13862 /* if player blocks last field, add delay for exactly one move */
13863 if (player->block_last_field)
13865 last_field_block_delay += player->move_delay_value;
13867 /* when blocking enabled, prevent moving up despite gravity */
13868 #if USE_PLAYER_GRAVITY
13869 if (player->gravity && player->MovDir == MV_UP)
13870 block_delay_adjustment = -1;
13872 if (game.gravity && player->MovDir == MV_UP)
13873 block_delay_adjustment = -1;
13877 /* add block delay adjustment (also possible when not blocking) */
13878 last_field_block_delay += block_delay_adjustment;
13880 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13881 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13884 #if USE_NEW_PLAYER_SPEED
13885 if (player->MovPos != 0) /* player has not yet reached destination */
13891 else if (!FrameReached(&player->actual_frame_counter, 1))
13894 #if USE_NEW_PLAYER_SPEED
13895 if (player->MovPos != 0)
13897 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13898 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13900 /* before DrawPlayer() to draw correct player graphic for this case */
13901 if (player->MovPos == 0)
13902 CheckGravityMovement(player);
13905 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13906 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13908 /* before DrawPlayer() to draw correct player graphic for this case */
13909 if (player->MovPos == 0)
13910 CheckGravityMovement(player);
13913 if (player->MovPos == 0) /* player reached destination field */
13915 if (player->move_delay_reset_counter > 0)
13917 player->move_delay_reset_counter--;
13919 if (player->move_delay_reset_counter == 0)
13921 /* continue with normal speed after quickly moving through gate */
13922 HALVE_PLAYER_SPEED(player);
13924 /* be able to make the next move without delay */
13925 player->move_delay = 0;
13929 player->last_jx = jx;
13930 player->last_jy = jy;
13932 if (Feld[jx][jy] == EL_EXIT_OPEN ||
13933 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13935 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
13937 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13938 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13940 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13942 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13943 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
13945 DrawPlayer(player); /* needed here only to cleanup last field */
13946 RemovePlayer(player);
13948 if (local_player->friends_still_needed == 0 ||
13949 IS_SP_ELEMENT(Feld[jx][jy]))
13950 PlayerWins(player);
13953 /* this breaks one level: "machine", level 000 */
13955 int move_direction = player->MovDir;
13956 int enter_side = MV_DIR_OPPOSITE(move_direction);
13957 int leave_side = move_direction;
13958 int old_jx = last_jx;
13959 int old_jy = last_jy;
13960 int old_element = Feld[old_jx][old_jy];
13961 int new_element = Feld[jx][jy];
13963 if (IS_CUSTOM_ELEMENT(old_element))
13964 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13966 player->index_bit, leave_side);
13968 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13969 CE_PLAYER_LEAVES_X,
13970 player->index_bit, leave_side);
13972 if (IS_CUSTOM_ELEMENT(new_element))
13973 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13974 player->index_bit, enter_side);
13976 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13977 CE_PLAYER_ENTERS_X,
13978 player->index_bit, enter_side);
13980 #if USE_FIX_CE_ACTION_WITH_PLAYER
13981 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13982 CE_MOVE_OF_X, move_direction);
13984 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13985 CE_MOVE_OF_X, move_direction);
13989 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13991 TestIfPlayerTouchesBadThing(jx, jy);
13992 TestIfPlayerTouchesCustomElement(jx, jy);
13994 /* needed because pushed element has not yet reached its destination,
13995 so it would trigger a change event at its previous field location */
13996 if (!player->is_pushing)
13997 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
13999 if (!player->active)
14000 RemovePlayer(player);
14003 if (!local_player->LevelSolved && level.use_step_counter)
14013 if (TimeLeft <= 10 && setup.time_limit)
14014 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14017 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14019 DisplayGameControlValues();
14021 DrawGameValue_Time(TimeLeft);
14024 if (!TimeLeft && setup.time_limit)
14025 for (i = 0; i < MAX_PLAYERS; i++)
14026 KillPlayer(&stored_player[i]);
14029 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
14031 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
14033 DisplayGameControlValues();
14036 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
14037 DrawGameValue_Time(TimePlayed);
14041 if (tape.single_step && tape.recording && !tape.pausing &&
14042 !player->programmed_action)
14043 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
14047 void ScrollScreen(struct PlayerInfo *player, int mode)
14049 static unsigned long screen_frame_counter = 0;
14051 if (mode == SCROLL_INIT)
14053 /* set scrolling step size according to actual player's moving speed */
14054 ScrollStepSize = TILEX / player->move_delay_value;
14056 screen_frame_counter = FrameCounter;
14057 ScreenMovDir = player->MovDir;
14058 ScreenMovPos = player->MovPos;
14059 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14062 else if (!FrameReached(&screen_frame_counter, 1))
14067 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
14068 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14069 redraw_mask |= REDRAW_FIELD;
14072 ScreenMovDir = MV_NONE;
14075 void TestIfPlayerTouchesCustomElement(int x, int y)
14077 static int xy[4][2] =
14084 static int trigger_sides[4][2] =
14086 /* center side border side */
14087 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14088 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14089 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14090 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14092 static int touch_dir[4] =
14094 MV_LEFT | MV_RIGHT,
14099 int center_element = Feld[x][y]; /* should always be non-moving! */
14102 for (i = 0; i < NUM_DIRECTIONS; i++)
14104 int xx = x + xy[i][0];
14105 int yy = y + xy[i][1];
14106 int center_side = trigger_sides[i][0];
14107 int border_side = trigger_sides[i][1];
14108 int border_element;
14110 if (!IN_LEV_FIELD(xx, yy))
14113 if (IS_PLAYER(x, y)) /* player found at center element */
14115 struct PlayerInfo *player = PLAYERINFO(x, y);
14117 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14118 border_element = Feld[xx][yy]; /* may be moving! */
14119 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14120 border_element = Feld[xx][yy];
14121 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14122 border_element = MovingOrBlocked2Element(xx, yy);
14124 continue; /* center and border element do not touch */
14126 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
14127 player->index_bit, border_side);
14128 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
14129 CE_PLAYER_TOUCHES_X,
14130 player->index_bit, border_side);
14132 #if USE_FIX_CE_ACTION_WITH_PLAYER
14134 /* use player element that is initially defined in the level playfield,
14135 not the player element that corresponds to the runtime player number
14136 (example: a level that contains EL_PLAYER_3 as the only player would
14137 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14138 int player_element = PLAYERINFO(x, y)->initial_element;
14140 CheckElementChangeBySide(xx, yy, border_element, player_element,
14141 CE_TOUCHING_X, border_side);
14145 else if (IS_PLAYER(xx, yy)) /* player found at border element */
14147 struct PlayerInfo *player = PLAYERINFO(xx, yy);
14149 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14151 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14152 continue; /* center and border element do not touch */
14155 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14156 player->index_bit, center_side);
14157 CheckTriggeredElementChangeByPlayer(x, y, center_element,
14158 CE_PLAYER_TOUCHES_X,
14159 player->index_bit, center_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(xx, yy)->initial_element;
14169 CheckElementChangeBySide(x, y, center_element, player_element,
14170 CE_TOUCHING_X, center_side);
14179 #if USE_ELEMENT_TOUCHING_BUGFIX
14181 void TestIfElementTouchesCustomElement(int x, int y)
14183 static int xy[4][2] =
14190 static int trigger_sides[4][2] =
14192 /* center side border side */
14193 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14194 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14195 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14196 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14198 static int touch_dir[4] =
14200 MV_LEFT | MV_RIGHT,
14205 boolean change_center_element = FALSE;
14206 int center_element = Feld[x][y]; /* should always be non-moving! */
14207 int border_element_old[NUM_DIRECTIONS];
14210 for (i = 0; i < NUM_DIRECTIONS; i++)
14212 int xx = x + xy[i][0];
14213 int yy = y + xy[i][1];
14214 int border_element;
14216 border_element_old[i] = -1;
14218 if (!IN_LEV_FIELD(xx, yy))
14221 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14222 border_element = Feld[xx][yy]; /* may be moving! */
14223 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14224 border_element = Feld[xx][yy];
14225 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14226 border_element = MovingOrBlocked2Element(xx, yy);
14228 continue; /* center and border element do not touch */
14230 border_element_old[i] = border_element;
14233 for (i = 0; i < NUM_DIRECTIONS; i++)
14235 int xx = x + xy[i][0];
14236 int yy = y + xy[i][1];
14237 int center_side = trigger_sides[i][0];
14238 int border_element = border_element_old[i];
14240 if (border_element == -1)
14243 /* check for change of border element */
14244 CheckElementChangeBySide(xx, yy, border_element, center_element,
14245 CE_TOUCHING_X, center_side);
14247 /* (center element cannot be player, so we dont have to check this here) */
14250 for (i = 0; i < NUM_DIRECTIONS; i++)
14252 int xx = x + xy[i][0];
14253 int yy = y + xy[i][1];
14254 int border_side = trigger_sides[i][1];
14255 int border_element = border_element_old[i];
14257 if (border_element == -1)
14260 /* check for change of center element (but change it only once) */
14261 if (!change_center_element)
14262 change_center_element =
14263 CheckElementChangeBySide(x, y, center_element, border_element,
14264 CE_TOUCHING_X, border_side);
14266 #if USE_FIX_CE_ACTION_WITH_PLAYER
14267 if (IS_PLAYER(xx, yy))
14269 /* use player element that is initially defined in the level playfield,
14270 not the player element that corresponds to the runtime player number
14271 (example: a level that contains EL_PLAYER_3 as the only player would
14272 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14273 int player_element = PLAYERINFO(xx, yy)->initial_element;
14275 CheckElementChangeBySide(x, y, center_element, player_element,
14276 CE_TOUCHING_X, border_side);
14284 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14286 static int xy[4][2] =
14293 static int trigger_sides[4][2] =
14295 /* center side border side */
14296 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14297 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14298 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14299 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14301 static int touch_dir[4] =
14303 MV_LEFT | MV_RIGHT,
14308 boolean change_center_element = FALSE;
14309 int center_element = Feld[x][y]; /* should always be non-moving! */
14312 for (i = 0; i < NUM_DIRECTIONS; i++)
14314 int xx = x + xy[i][0];
14315 int yy = y + xy[i][1];
14316 int center_side = trigger_sides[i][0];
14317 int border_side = trigger_sides[i][1];
14318 int border_element;
14320 if (!IN_LEV_FIELD(xx, yy))
14323 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14324 border_element = Feld[xx][yy]; /* may be moving! */
14325 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14326 border_element = Feld[xx][yy];
14327 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14328 border_element = MovingOrBlocked2Element(xx, yy);
14330 continue; /* center and border element do not touch */
14332 /* check for change of center element (but change it only once) */
14333 if (!change_center_element)
14334 change_center_element =
14335 CheckElementChangeBySide(x, y, center_element, border_element,
14336 CE_TOUCHING_X, border_side);
14338 /* check for change of border element */
14339 CheckElementChangeBySide(xx, yy, border_element, center_element,
14340 CE_TOUCHING_X, center_side);
14346 void TestIfElementHitsCustomElement(int x, int y, int direction)
14348 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14349 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14350 int hitx = x + dx, hity = y + dy;
14351 int hitting_element = Feld[x][y];
14352 int touched_element;
14354 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14357 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14358 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14360 if (IN_LEV_FIELD(hitx, hity))
14362 int opposite_direction = MV_DIR_OPPOSITE(direction);
14363 int hitting_side = direction;
14364 int touched_side = opposite_direction;
14365 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14366 MovDir[hitx][hity] != direction ||
14367 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14373 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14374 CE_HITTING_X, touched_side);
14376 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14377 CE_HIT_BY_X, hitting_side);
14379 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14380 CE_HIT_BY_SOMETHING, opposite_direction);
14382 #if USE_FIX_CE_ACTION_WITH_PLAYER
14383 if (IS_PLAYER(hitx, hity))
14385 /* use player element that is initially defined in the level playfield,
14386 not the player element that corresponds to the runtime player number
14387 (example: a level that contains EL_PLAYER_3 as the only player would
14388 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14389 int player_element = PLAYERINFO(hitx, hity)->initial_element;
14391 CheckElementChangeBySide(x, y, hitting_element, player_element,
14392 CE_HITTING_X, touched_side);
14398 /* "hitting something" is also true when hitting the playfield border */
14399 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14400 CE_HITTING_SOMETHING, direction);
14404 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14406 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14407 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14408 int hitx = x + dx, hity = y + dy;
14409 int hitting_element = Feld[x][y];
14410 int touched_element;
14412 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14413 !IS_FREE(hitx, hity) &&
14414 (!IS_MOVING(hitx, hity) ||
14415 MovDir[hitx][hity] != direction ||
14416 ABS(MovPos[hitx][hity]) <= TILEY / 2));
14419 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14423 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14427 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14428 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14430 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14431 EP_CAN_SMASH_EVERYTHING, direction);
14433 if (IN_LEV_FIELD(hitx, hity))
14435 int opposite_direction = MV_DIR_OPPOSITE(direction);
14436 int hitting_side = direction;
14437 int touched_side = opposite_direction;
14439 int touched_element = MovingOrBlocked2Element(hitx, hity);
14442 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14443 MovDir[hitx][hity] != direction ||
14444 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14453 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14454 CE_SMASHED_BY_SOMETHING, opposite_direction);
14456 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14457 CE_OTHER_IS_SMASHING, touched_side);
14459 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14460 CE_OTHER_GETS_SMASHED, hitting_side);
14466 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14468 int i, kill_x = -1, kill_y = -1;
14470 int bad_element = -1;
14471 static int test_xy[4][2] =
14478 static int test_dir[4] =
14486 for (i = 0; i < NUM_DIRECTIONS; i++)
14488 int test_x, test_y, test_move_dir, test_element;
14490 test_x = good_x + test_xy[i][0];
14491 test_y = good_y + test_xy[i][1];
14493 if (!IN_LEV_FIELD(test_x, test_y))
14497 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14499 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14501 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14502 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14504 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14505 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
14509 bad_element = test_element;
14515 if (kill_x != -1 || kill_y != -1)
14517 if (IS_PLAYER(good_x, good_y))
14519 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14521 if (player->shield_deadly_time_left > 0 &&
14522 !IS_INDESTRUCTIBLE(bad_element))
14523 Bang(kill_x, kill_y);
14524 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14525 KillPlayer(player);
14528 Bang(good_x, good_y);
14532 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14534 int i, kill_x = -1, kill_y = -1;
14535 int bad_element = Feld[bad_x][bad_y];
14536 static int test_xy[4][2] =
14543 static int touch_dir[4] =
14545 MV_LEFT | MV_RIGHT,
14550 static int test_dir[4] =
14558 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
14561 for (i = 0; i < NUM_DIRECTIONS; i++)
14563 int test_x, test_y, test_move_dir, test_element;
14565 test_x = bad_x + test_xy[i][0];
14566 test_y = bad_y + test_xy[i][1];
14568 if (!IN_LEV_FIELD(test_x, test_y))
14572 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14574 test_element = Feld[test_x][test_y];
14576 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14577 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14579 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
14580 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
14582 /* good thing is player or penguin that does not move away */
14583 if (IS_PLAYER(test_x, test_y))
14585 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14587 if (bad_element == EL_ROBOT && player->is_moving)
14588 continue; /* robot does not kill player if he is moving */
14590 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14592 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14593 continue; /* center and border element do not touch */
14601 else if (test_element == EL_PENGUIN)
14611 if (kill_x != -1 || kill_y != -1)
14613 if (IS_PLAYER(kill_x, kill_y))
14615 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14617 if (player->shield_deadly_time_left > 0 &&
14618 !IS_INDESTRUCTIBLE(bad_element))
14619 Bang(bad_x, bad_y);
14620 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14621 KillPlayer(player);
14624 Bang(kill_x, kill_y);
14628 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14630 int bad_element = Feld[bad_x][bad_y];
14631 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14632 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
14633 int test_x = bad_x + dx, test_y = bad_y + dy;
14634 int test_move_dir, test_element;
14635 int kill_x = -1, kill_y = -1;
14637 if (!IN_LEV_FIELD(test_x, test_y))
14641 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14643 test_element = Feld[test_x][test_y];
14645 if (test_move_dir != bad_move_dir)
14647 /* good thing can be player or penguin that does not move away */
14648 if (IS_PLAYER(test_x, test_y))
14650 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14652 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14653 player as being hit when he is moving towards the bad thing, because
14654 the "get hit by" condition would be lost after the player stops) */
14655 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14656 return; /* player moves away from bad thing */
14661 else if (test_element == EL_PENGUIN)
14668 if (kill_x != -1 || kill_y != -1)
14670 if (IS_PLAYER(kill_x, kill_y))
14672 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14674 if (player->shield_deadly_time_left > 0 &&
14675 !IS_INDESTRUCTIBLE(bad_element))
14676 Bang(bad_x, bad_y);
14677 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14678 KillPlayer(player);
14681 Bang(kill_x, kill_y);
14685 void TestIfPlayerTouchesBadThing(int x, int y)
14687 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14690 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14692 TestIfGoodThingHitsBadThing(x, y, move_dir);
14695 void TestIfBadThingTouchesPlayer(int x, int y)
14697 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14700 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14702 TestIfBadThingHitsGoodThing(x, y, move_dir);
14705 void TestIfFriendTouchesBadThing(int x, int y)
14707 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14710 void TestIfBadThingTouchesFriend(int x, int y)
14712 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14715 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14717 int i, kill_x = bad_x, kill_y = bad_y;
14718 static int xy[4][2] =
14726 for (i = 0; i < NUM_DIRECTIONS; i++)
14730 x = bad_x + xy[i][0];
14731 y = bad_y + xy[i][1];
14732 if (!IN_LEV_FIELD(x, y))
14735 element = Feld[x][y];
14736 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14737 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14745 if (kill_x != bad_x || kill_y != bad_y)
14746 Bang(bad_x, bad_y);
14749 void KillPlayer(struct PlayerInfo *player)
14751 int jx = player->jx, jy = player->jy;
14753 if (!player->active)
14757 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14758 player->killed, player->active, player->reanimated);
14761 /* the following code was introduced to prevent an infinite loop when calling
14763 -> CheckTriggeredElementChangeExt()
14764 -> ExecuteCustomElementAction()
14766 -> (infinitely repeating the above sequence of function calls)
14767 which occurs when killing the player while having a CE with the setting
14768 "kill player X when explosion of <player X>"; the solution using a new
14769 field "player->killed" was chosen for backwards compatibility, although
14770 clever use of the fields "player->active" etc. would probably also work */
14772 if (player->killed)
14776 player->killed = TRUE;
14778 /* remove accessible field at the player's position */
14779 Feld[jx][jy] = EL_EMPTY;
14781 /* deactivate shield (else Bang()/Explode() would not work right) */
14782 player->shield_normal_time_left = 0;
14783 player->shield_deadly_time_left = 0;
14786 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14787 player->killed, player->active, player->reanimated);
14793 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14794 player->killed, player->active, player->reanimated);
14797 #if USE_PLAYER_REANIMATION
14799 if (player->reanimated) /* killed player may have been reanimated */
14800 player->killed = player->reanimated = FALSE;
14802 BuryPlayer(player);
14804 if (player->killed) /* player may have been reanimated */
14805 BuryPlayer(player);
14808 BuryPlayer(player);
14812 static void KillPlayerUnlessEnemyProtected(int x, int y)
14814 if (!PLAYER_ENEMY_PROTECTED(x, y))
14815 KillPlayer(PLAYERINFO(x, y));
14818 static void KillPlayerUnlessExplosionProtected(int x, int y)
14820 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14821 KillPlayer(PLAYERINFO(x, y));
14824 void BuryPlayer(struct PlayerInfo *player)
14826 int jx = player->jx, jy = player->jy;
14828 if (!player->active)
14831 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14832 PlayLevelSound(jx, jy, SND_GAME_LOSING);
14834 player->GameOver = TRUE;
14835 RemovePlayer(player);
14838 void RemovePlayer(struct PlayerInfo *player)
14840 int jx = player->jx, jy = player->jy;
14841 int i, found = FALSE;
14843 player->present = FALSE;
14844 player->active = FALSE;
14846 if (!ExplodeField[jx][jy])
14847 StorePlayer[jx][jy] = 0;
14849 if (player->is_moving)
14850 TEST_DrawLevelField(player->last_jx, player->last_jy);
14852 for (i = 0; i < MAX_PLAYERS; i++)
14853 if (stored_player[i].active)
14857 AllPlayersGone = TRUE;
14863 #if USE_NEW_SNAP_DELAY
14864 static void setFieldForSnapping(int x, int y, int element, int direction)
14866 struct ElementInfo *ei = &element_info[element];
14867 int direction_bit = MV_DIR_TO_BIT(direction);
14868 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14869 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14870 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14872 Feld[x][y] = EL_ELEMENT_SNAPPING;
14873 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14875 ResetGfxAnimation(x, y);
14877 GfxElement[x][y] = element;
14878 GfxAction[x][y] = action;
14879 GfxDir[x][y] = direction;
14880 GfxFrame[x][y] = -1;
14885 =============================================================================
14886 checkDiagonalPushing()
14887 -----------------------------------------------------------------------------
14888 check if diagonal input device direction results in pushing of object
14889 (by checking if the alternative direction is walkable, diggable, ...)
14890 =============================================================================
14893 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14894 int x, int y, int real_dx, int real_dy)
14896 int jx, jy, dx, dy, xx, yy;
14898 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
14901 /* diagonal direction: check alternative direction */
14906 xx = jx + (dx == 0 ? real_dx : 0);
14907 yy = jy + (dy == 0 ? real_dy : 0);
14909 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14913 =============================================================================
14915 -----------------------------------------------------------------------------
14916 x, y: field next to player (non-diagonal) to try to dig to
14917 real_dx, real_dy: direction as read from input device (can be diagonal)
14918 =============================================================================
14921 static int DigField(struct PlayerInfo *player,
14922 int oldx, int oldy, int x, int y,
14923 int real_dx, int real_dy, int mode)
14925 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14926 boolean player_was_pushing = player->is_pushing;
14927 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14928 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14929 int jx = oldx, jy = oldy;
14930 int dx = x - jx, dy = y - jy;
14931 int nextx = x + dx, nexty = y + dy;
14932 int move_direction = (dx == -1 ? MV_LEFT :
14933 dx == +1 ? MV_RIGHT :
14935 dy == +1 ? MV_DOWN : MV_NONE);
14936 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14937 int dig_side = MV_DIR_OPPOSITE(move_direction);
14938 int old_element = Feld[jx][jy];
14939 #if USE_FIXED_DONT_RUN_INTO
14940 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14946 if (is_player) /* function can also be called by EL_PENGUIN */
14948 if (player->MovPos == 0)
14950 player->is_digging = FALSE;
14951 player->is_collecting = FALSE;
14954 if (player->MovPos == 0) /* last pushing move finished */
14955 player->is_pushing = FALSE;
14957 if (mode == DF_NO_PUSH) /* player just stopped pushing */
14959 player->is_switching = FALSE;
14960 player->push_delay = -1;
14962 return MP_NO_ACTION;
14966 #if !USE_FIXED_DONT_RUN_INTO
14967 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14968 return MP_NO_ACTION;
14971 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14972 old_element = Back[jx][jy];
14974 /* in case of element dropped at player position, check background */
14975 else if (Back[jx][jy] != EL_EMPTY &&
14976 game.engine_version >= VERSION_IDENT(2,2,0,0))
14977 old_element = Back[jx][jy];
14979 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14980 return MP_NO_ACTION; /* field has no opening in this direction */
14982 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14983 return MP_NO_ACTION; /* field has no opening in this direction */
14985 #if USE_FIXED_DONT_RUN_INTO
14986 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14990 Feld[jx][jy] = player->artwork_element;
14991 InitMovingField(jx, jy, MV_DOWN);
14992 Store[jx][jy] = EL_ACID;
14993 ContinueMoving(jx, jy);
14994 BuryPlayer(player);
14996 return MP_DONT_RUN_INTO;
15000 #if USE_FIXED_DONT_RUN_INTO
15001 if (player_can_move && DONT_RUN_INTO(element))
15003 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
15005 return MP_DONT_RUN_INTO;
15009 #if USE_FIXED_DONT_RUN_INTO
15010 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
15011 return MP_NO_ACTION;
15014 #if !USE_FIXED_DONT_RUN_INTO
15015 element = Feld[x][y];
15018 collect_count = element_info[element].collect_count_initial;
15020 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
15021 return MP_NO_ACTION;
15023 if (game.engine_version < VERSION_IDENT(2,2,0,0))
15024 player_can_move = player_can_move_or_snap;
15026 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
15027 game.engine_version >= VERSION_IDENT(2,2,0,0))
15029 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
15030 player->index_bit, dig_side);
15031 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15032 player->index_bit, dig_side);
15034 if (element == EL_DC_LANDMINE)
15037 if (Feld[x][y] != element) /* field changed by snapping */
15040 return MP_NO_ACTION;
15043 #if USE_PLAYER_GRAVITY
15044 if (player->gravity && is_player && !player->is_auto_moving &&
15045 canFallDown(player) && move_direction != MV_DOWN &&
15046 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15047 return MP_NO_ACTION; /* player cannot walk here due to gravity */
15049 if (game.gravity && is_player && !player->is_auto_moving &&
15050 canFallDown(player) && move_direction != MV_DOWN &&
15051 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15052 return MP_NO_ACTION; /* player cannot walk here due to gravity */
15055 if (player_can_move &&
15056 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
15058 int sound_element = SND_ELEMENT(element);
15059 int sound_action = ACTION_WALKING;
15061 if (IS_RND_GATE(element))
15063 if (!player->key[RND_GATE_NR(element)])
15064 return MP_NO_ACTION;
15066 else if (IS_RND_GATE_GRAY(element))
15068 if (!player->key[RND_GATE_GRAY_NR(element)])
15069 return MP_NO_ACTION;
15071 else if (IS_RND_GATE_GRAY_ACTIVE(element))
15073 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
15074 return MP_NO_ACTION;
15076 else if (element == EL_EXIT_OPEN ||
15077 element == EL_EM_EXIT_OPEN ||
15079 element == EL_EM_EXIT_OPENING ||
15081 element == EL_STEEL_EXIT_OPEN ||
15082 element == EL_EM_STEEL_EXIT_OPEN ||
15084 element == EL_EM_STEEL_EXIT_OPENING ||
15086 element == EL_SP_EXIT_OPEN ||
15087 element == EL_SP_EXIT_OPENING)
15089 sound_action = ACTION_PASSING; /* player is passing exit */
15091 else if (element == EL_EMPTY)
15093 sound_action = ACTION_MOVING; /* nothing to walk on */
15096 /* play sound from background or player, whatever is available */
15097 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
15098 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
15100 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
15102 else if (player_can_move &&
15103 IS_PASSABLE(element) && canPassField(x, y, move_direction))
15105 if (!ACCESS_FROM(element, opposite_direction))
15106 return MP_NO_ACTION; /* field not accessible from this direction */
15108 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
15109 return MP_NO_ACTION;
15111 if (IS_EM_GATE(element))
15113 if (!player->key[EM_GATE_NR(element)])
15114 return MP_NO_ACTION;
15116 else if (IS_EM_GATE_GRAY(element))
15118 if (!player->key[EM_GATE_GRAY_NR(element)])
15119 return MP_NO_ACTION;
15121 else if (IS_EM_GATE_GRAY_ACTIVE(element))
15123 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
15124 return MP_NO_ACTION;
15126 else if (IS_EMC_GATE(element))
15128 if (!player->key[EMC_GATE_NR(element)])
15129 return MP_NO_ACTION;
15131 else if (IS_EMC_GATE_GRAY(element))
15133 if (!player->key[EMC_GATE_GRAY_NR(element)])
15134 return MP_NO_ACTION;
15136 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
15138 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
15139 return MP_NO_ACTION;
15141 else if (element == EL_DC_GATE_WHITE ||
15142 element == EL_DC_GATE_WHITE_GRAY ||
15143 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15145 if (player->num_white_keys == 0)
15146 return MP_NO_ACTION;
15148 player->num_white_keys--;
15150 else if (IS_SP_PORT(element))
15152 if (element == EL_SP_GRAVITY_PORT_LEFT ||
15153 element == EL_SP_GRAVITY_PORT_RIGHT ||
15154 element == EL_SP_GRAVITY_PORT_UP ||
15155 element == EL_SP_GRAVITY_PORT_DOWN)
15156 #if USE_PLAYER_GRAVITY
15157 player->gravity = !player->gravity;
15159 game.gravity = !game.gravity;
15161 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15162 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15163 element == EL_SP_GRAVITY_ON_PORT_UP ||
15164 element == EL_SP_GRAVITY_ON_PORT_DOWN)
15165 #if USE_PLAYER_GRAVITY
15166 player->gravity = TRUE;
15168 game.gravity = TRUE;
15170 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15171 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15172 element == EL_SP_GRAVITY_OFF_PORT_UP ||
15173 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15174 #if USE_PLAYER_GRAVITY
15175 player->gravity = FALSE;
15177 game.gravity = FALSE;
15181 /* automatically move to the next field with double speed */
15182 player->programmed_action = move_direction;
15184 if (player->move_delay_reset_counter == 0)
15186 player->move_delay_reset_counter = 2; /* two double speed steps */
15188 DOUBLE_PLAYER_SPEED(player);
15191 PlayLevelSoundAction(x, y, ACTION_PASSING);
15193 else if (player_can_move_or_snap && IS_DIGGABLE(element))
15197 if (mode != DF_SNAP)
15199 GfxElement[x][y] = GFX_ELEMENT(element);
15200 player->is_digging = TRUE;
15203 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15205 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15206 player->index_bit, dig_side);
15208 if (mode == DF_SNAP)
15210 #if USE_NEW_SNAP_DELAY
15211 if (level.block_snap_field)
15212 setFieldForSnapping(x, y, element, move_direction);
15214 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15216 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15219 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15220 player->index_bit, dig_side);
15223 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15227 if (is_player && mode != DF_SNAP)
15229 GfxElement[x][y] = element;
15230 player->is_collecting = TRUE;
15233 if (element == EL_SPEED_PILL)
15235 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15237 else if (element == EL_EXTRA_TIME && level.time > 0)
15239 TimeLeft += level.extra_time;
15242 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15244 DisplayGameControlValues();
15246 DrawGameValue_Time(TimeLeft);
15249 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15251 player->shield_normal_time_left += level.shield_normal_time;
15252 if (element == EL_SHIELD_DEADLY)
15253 player->shield_deadly_time_left += level.shield_deadly_time;
15255 else if (element == EL_DYNAMITE ||
15256 element == EL_EM_DYNAMITE ||
15257 element == EL_SP_DISK_RED)
15259 if (player->inventory_size < MAX_INVENTORY_SIZE)
15260 player->inventory_element[player->inventory_size++] = element;
15262 DrawGameDoorValues();
15264 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15266 player->dynabomb_count++;
15267 player->dynabombs_left++;
15269 else if (element == EL_DYNABOMB_INCREASE_SIZE)
15271 player->dynabomb_size++;
15273 else if (element == EL_DYNABOMB_INCREASE_POWER)
15275 player->dynabomb_xl = TRUE;
15277 else if (IS_KEY(element))
15279 player->key[KEY_NR(element)] = TRUE;
15281 DrawGameDoorValues();
15283 else if (element == EL_DC_KEY_WHITE)
15285 player->num_white_keys++;
15287 /* display white keys? */
15288 /* DrawGameDoorValues(); */
15290 else if (IS_ENVELOPE(element))
15292 player->show_envelope = element;
15294 else if (element == EL_EMC_LENSES)
15296 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15298 RedrawAllInvisibleElementsForLenses();
15300 else if (element == EL_EMC_MAGNIFIER)
15302 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15304 RedrawAllInvisibleElementsForMagnifier();
15306 else if (IS_DROPPABLE(element) ||
15307 IS_THROWABLE(element)) /* can be collected and dropped */
15311 if (collect_count == 0)
15312 player->inventory_infinite_element = element;
15314 for (i = 0; i < collect_count; i++)
15315 if (player->inventory_size < MAX_INVENTORY_SIZE)
15316 player->inventory_element[player->inventory_size++] = element;
15318 DrawGameDoorValues();
15320 else if (collect_count > 0)
15322 local_player->gems_still_needed -= collect_count;
15323 if (local_player->gems_still_needed < 0)
15324 local_player->gems_still_needed = 0;
15327 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15329 DisplayGameControlValues();
15331 DrawGameValue_Emeralds(local_player->gems_still_needed);
15335 RaiseScoreElement(element);
15336 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15339 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15340 player->index_bit, dig_side);
15342 if (mode == DF_SNAP)
15344 #if USE_NEW_SNAP_DELAY
15345 if (level.block_snap_field)
15346 setFieldForSnapping(x, y, element, move_direction);
15348 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15350 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15353 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15354 player->index_bit, dig_side);
15357 else if (player_can_move_or_snap && IS_PUSHABLE(element))
15359 if (mode == DF_SNAP && element != EL_BD_ROCK)
15360 return MP_NO_ACTION;
15362 if (CAN_FALL(element) && dy)
15363 return MP_NO_ACTION;
15365 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15366 !(element == EL_SPRING && level.use_spring_bug))
15367 return MP_NO_ACTION;
15369 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15370 ((move_direction & MV_VERTICAL &&
15371 ((element_info[element].move_pattern & MV_LEFT &&
15372 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15373 (element_info[element].move_pattern & MV_RIGHT &&
15374 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15375 (move_direction & MV_HORIZONTAL &&
15376 ((element_info[element].move_pattern & MV_UP &&
15377 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15378 (element_info[element].move_pattern & MV_DOWN &&
15379 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15380 return MP_NO_ACTION;
15382 /* do not push elements already moving away faster than player */
15383 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15384 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15385 return MP_NO_ACTION;
15387 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15389 if (player->push_delay_value == -1 || !player_was_pushing)
15390 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15392 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15394 if (player->push_delay_value == -1)
15395 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15397 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15399 if (!player->is_pushing)
15400 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15403 player->is_pushing = TRUE;
15404 player->is_active = TRUE;
15406 if (!(IN_LEV_FIELD(nextx, nexty) &&
15407 (IS_FREE(nextx, nexty) ||
15408 (IS_SB_ELEMENT(element) &&
15409 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15410 (IS_CUSTOM_ELEMENT(element) &&
15411 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15412 return MP_NO_ACTION;
15414 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15415 return MP_NO_ACTION;
15417 if (player->push_delay == -1) /* new pushing; restart delay */
15418 player->push_delay = 0;
15420 if (player->push_delay < player->push_delay_value &&
15421 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15422 element != EL_SPRING && element != EL_BALLOON)
15424 /* make sure that there is no move delay before next try to push */
15425 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15426 player->move_delay = 0;
15428 return MP_NO_ACTION;
15431 if (IS_CUSTOM_ELEMENT(element) &&
15432 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15434 if (!DigFieldByCE(nextx, nexty, element))
15435 return MP_NO_ACTION;
15438 if (IS_SB_ELEMENT(element))
15440 if (element == EL_SOKOBAN_FIELD_FULL)
15442 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15443 local_player->sokobanfields_still_needed++;
15446 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15448 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15449 local_player->sokobanfields_still_needed--;
15452 Feld[x][y] = EL_SOKOBAN_OBJECT;
15454 if (Back[x][y] == Back[nextx][nexty])
15455 PlayLevelSoundAction(x, y, ACTION_PUSHING);
15456 else if (Back[x][y] != 0)
15457 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15460 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15464 if (local_player->sokobanfields_still_needed == 0 &&
15465 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
15467 if (local_player->sokobanfields_still_needed == 0 &&
15468 game.emulation == EMU_SOKOBAN)
15471 PlayerWins(player);
15473 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15477 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15479 InitMovingField(x, y, move_direction);
15480 GfxAction[x][y] = ACTION_PUSHING;
15482 if (mode == DF_SNAP)
15483 ContinueMoving(x, y);
15485 MovPos[x][y] = (dx != 0 ? dx : dy);
15487 Pushed[x][y] = TRUE;
15488 Pushed[nextx][nexty] = TRUE;
15490 if (game.engine_version < VERSION_IDENT(2,2,0,7))
15491 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15493 player->push_delay_value = -1; /* get new value later */
15495 /* check for element change _after_ element has been pushed */
15496 if (game.use_change_when_pushing_bug)
15498 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15499 player->index_bit, dig_side);
15500 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15501 player->index_bit, dig_side);
15504 else if (IS_SWITCHABLE(element))
15506 if (PLAYER_SWITCHING(player, x, y))
15508 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15509 player->index_bit, dig_side);
15514 player->is_switching = TRUE;
15515 player->switch_x = x;
15516 player->switch_y = y;
15518 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15520 if (element == EL_ROBOT_WHEEL)
15522 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15526 game.robot_wheel_active = TRUE;
15528 TEST_DrawLevelField(x, y);
15530 else if (element == EL_SP_TERMINAL)
15534 SCAN_PLAYFIELD(xx, yy)
15536 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15538 else if (Feld[xx][yy] == EL_SP_TERMINAL)
15539 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15542 else if (IS_BELT_SWITCH(element))
15544 ToggleBeltSwitch(x, y);
15546 else if (element == EL_SWITCHGATE_SWITCH_UP ||
15547 element == EL_SWITCHGATE_SWITCH_DOWN ||
15548 element == EL_DC_SWITCHGATE_SWITCH_UP ||
15549 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15551 ToggleSwitchgateSwitch(x, y);
15553 else if (element == EL_LIGHT_SWITCH ||
15554 element == EL_LIGHT_SWITCH_ACTIVE)
15556 ToggleLightSwitch(x, y);
15558 else if (element == EL_TIMEGATE_SWITCH ||
15559 element == EL_DC_TIMEGATE_SWITCH)
15561 ActivateTimegateSwitch(x, y);
15563 else if (element == EL_BALLOON_SWITCH_LEFT ||
15564 element == EL_BALLOON_SWITCH_RIGHT ||
15565 element == EL_BALLOON_SWITCH_UP ||
15566 element == EL_BALLOON_SWITCH_DOWN ||
15567 element == EL_BALLOON_SWITCH_NONE ||
15568 element == EL_BALLOON_SWITCH_ANY)
15570 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
15571 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15572 element == EL_BALLOON_SWITCH_UP ? MV_UP :
15573 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
15574 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
15577 else if (element == EL_LAMP)
15579 Feld[x][y] = EL_LAMP_ACTIVE;
15580 local_player->lights_still_needed--;
15582 ResetGfxAnimation(x, y);
15583 TEST_DrawLevelField(x, y);
15585 else if (element == EL_TIME_ORB_FULL)
15587 Feld[x][y] = EL_TIME_ORB_EMPTY;
15589 if (level.time > 0 || level.use_time_orb_bug)
15591 TimeLeft += level.time_orb_time;
15594 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15596 DisplayGameControlValues();
15598 DrawGameValue_Time(TimeLeft);
15602 ResetGfxAnimation(x, y);
15603 TEST_DrawLevelField(x, y);
15605 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15606 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15610 game.ball_state = !game.ball_state;
15612 SCAN_PLAYFIELD(xx, yy)
15614 int e = Feld[xx][yy];
15616 if (game.ball_state)
15618 if (e == EL_EMC_MAGIC_BALL)
15619 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15620 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15621 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15625 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15626 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15627 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15628 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15633 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15634 player->index_bit, dig_side);
15636 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15637 player->index_bit, dig_side);
15639 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15640 player->index_bit, dig_side);
15646 if (!PLAYER_SWITCHING(player, x, y))
15648 player->is_switching = TRUE;
15649 player->switch_x = x;
15650 player->switch_y = y;
15652 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15653 player->index_bit, dig_side);
15654 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15655 player->index_bit, dig_side);
15657 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15658 player->index_bit, dig_side);
15659 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15660 player->index_bit, dig_side);
15663 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15664 player->index_bit, dig_side);
15665 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15666 player->index_bit, dig_side);
15668 return MP_NO_ACTION;
15671 player->push_delay = -1;
15673 if (is_player) /* function can also be called by EL_PENGUIN */
15675 if (Feld[x][y] != element) /* really digged/collected something */
15677 player->is_collecting = !player->is_digging;
15678 player->is_active = TRUE;
15685 static boolean DigFieldByCE(int x, int y, int digging_element)
15687 int element = Feld[x][y];
15689 if (!IS_FREE(x, y))
15691 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15692 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15695 /* no element can dig solid indestructible elements */
15696 if (IS_INDESTRUCTIBLE(element) &&
15697 !IS_DIGGABLE(element) &&
15698 !IS_COLLECTIBLE(element))
15701 if (AmoebaNr[x][y] &&
15702 (element == EL_AMOEBA_FULL ||
15703 element == EL_BD_AMOEBA ||
15704 element == EL_AMOEBA_GROWING))
15706 AmoebaCnt[AmoebaNr[x][y]]--;
15707 AmoebaCnt2[AmoebaNr[x][y]]--;
15710 if (IS_MOVING(x, y))
15711 RemoveMovingField(x, y);
15715 TEST_DrawLevelField(x, y);
15718 /* if digged element was about to explode, prevent the explosion */
15719 ExplodeField[x][y] = EX_TYPE_NONE;
15721 PlayLevelSoundAction(x, y, action);
15724 Store[x][y] = EL_EMPTY;
15727 /* this makes it possible to leave the removed element again */
15728 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15729 Store[x][y] = element;
15731 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15733 int move_leave_element = element_info[digging_element].move_leave_element;
15735 /* this makes it possible to leave the removed element again */
15736 Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15737 element : move_leave_element);
15744 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15746 int jx = player->jx, jy = player->jy;
15747 int x = jx + dx, y = jy + dy;
15748 int snap_direction = (dx == -1 ? MV_LEFT :
15749 dx == +1 ? MV_RIGHT :
15751 dy == +1 ? MV_DOWN : MV_NONE);
15752 boolean can_continue_snapping = (level.continuous_snapping &&
15753 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15755 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15758 if (!player->active || !IN_LEV_FIELD(x, y))
15766 if (player->MovPos == 0)
15767 player->is_pushing = FALSE;
15769 player->is_snapping = FALSE;
15771 if (player->MovPos == 0)
15773 player->is_moving = FALSE;
15774 player->is_digging = FALSE;
15775 player->is_collecting = FALSE;
15781 #if USE_NEW_CONTINUOUS_SNAPPING
15782 /* prevent snapping with already pressed snap key when not allowed */
15783 if (player->is_snapping && !can_continue_snapping)
15786 if (player->is_snapping)
15790 player->MovDir = snap_direction;
15792 if (player->MovPos == 0)
15794 player->is_moving = FALSE;
15795 player->is_digging = FALSE;
15796 player->is_collecting = FALSE;
15799 player->is_dropping = FALSE;
15800 player->is_dropping_pressed = FALSE;
15801 player->drop_pressed_delay = 0;
15803 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15806 player->is_snapping = TRUE;
15807 player->is_active = TRUE;
15809 if (player->MovPos == 0)
15811 player->is_moving = FALSE;
15812 player->is_digging = FALSE;
15813 player->is_collecting = FALSE;
15816 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
15817 TEST_DrawLevelField(player->last_jx, player->last_jy);
15819 TEST_DrawLevelField(x, y);
15824 static boolean DropElement(struct PlayerInfo *player)
15826 int old_element, new_element;
15827 int dropx = player->jx, dropy = player->jy;
15828 int drop_direction = player->MovDir;
15829 int drop_side = drop_direction;
15831 int drop_element = get_next_dropped_element(player);
15833 int drop_element = (player->inventory_size > 0 ?
15834 player->inventory_element[player->inventory_size - 1] :
15835 player->inventory_infinite_element != EL_UNDEFINED ?
15836 player->inventory_infinite_element :
15837 player->dynabombs_left > 0 ?
15838 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15842 player->is_dropping_pressed = TRUE;
15844 /* do not drop an element on top of another element; when holding drop key
15845 pressed without moving, dropped element must move away before the next
15846 element can be dropped (this is especially important if the next element
15847 is dynamite, which can be placed on background for historical reasons) */
15848 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15851 if (IS_THROWABLE(drop_element))
15853 dropx += GET_DX_FROM_DIR(drop_direction);
15854 dropy += GET_DY_FROM_DIR(drop_direction);
15856 if (!IN_LEV_FIELD(dropx, dropy))
15860 old_element = Feld[dropx][dropy]; /* old element at dropping position */
15861 new_element = drop_element; /* default: no change when dropping */
15863 /* check if player is active, not moving and ready to drop */
15864 if (!player->active || player->MovPos || player->drop_delay > 0)
15867 /* check if player has anything that can be dropped */
15868 if (new_element == EL_UNDEFINED)
15871 /* check if drop key was pressed long enough for EM style dynamite */
15872 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15875 /* check if anything can be dropped at the current position */
15876 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15879 /* collected custom elements can only be dropped on empty fields */
15880 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15883 if (old_element != EL_EMPTY)
15884 Back[dropx][dropy] = old_element; /* store old element on this field */
15886 ResetGfxAnimation(dropx, dropy);
15887 ResetRandomAnimationValue(dropx, dropy);
15889 if (player->inventory_size > 0 ||
15890 player->inventory_infinite_element != EL_UNDEFINED)
15892 if (player->inventory_size > 0)
15894 player->inventory_size--;
15896 DrawGameDoorValues();
15898 if (new_element == EL_DYNAMITE)
15899 new_element = EL_DYNAMITE_ACTIVE;
15900 else if (new_element == EL_EM_DYNAMITE)
15901 new_element = EL_EM_DYNAMITE_ACTIVE;
15902 else if (new_element == EL_SP_DISK_RED)
15903 new_element = EL_SP_DISK_RED_ACTIVE;
15906 Feld[dropx][dropy] = new_element;
15908 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15909 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15910 el2img(Feld[dropx][dropy]), 0);
15912 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15914 /* needed if previous element just changed to "empty" in the last frame */
15915 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15917 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15918 player->index_bit, drop_side);
15919 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15921 player->index_bit, drop_side);
15923 TestIfElementTouchesCustomElement(dropx, dropy);
15925 else /* player is dropping a dyna bomb */
15927 player->dynabombs_left--;
15929 Feld[dropx][dropy] = new_element;
15931 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15932 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15933 el2img(Feld[dropx][dropy]), 0);
15935 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15938 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15939 InitField_WithBug1(dropx, dropy, FALSE);
15941 new_element = Feld[dropx][dropy]; /* element might have changed */
15943 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15944 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15946 int move_direction, nextx, nexty;
15948 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15949 MovDir[dropx][dropy] = drop_direction;
15951 move_direction = MovDir[dropx][dropy];
15952 nextx = dropx + GET_DX_FROM_DIR(move_direction);
15953 nexty = dropy + GET_DY_FROM_DIR(move_direction);
15955 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15957 #if USE_FIX_IMPACT_COLLISION
15958 /* do not cause impact style collision by dropping elements that can fall */
15959 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15961 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15965 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15966 player->is_dropping = TRUE;
15968 player->drop_pressed_delay = 0;
15969 player->is_dropping_pressed = FALSE;
15971 player->drop_x = dropx;
15972 player->drop_y = dropy;
15977 /* ------------------------------------------------------------------------- */
15978 /* game sound playing functions */
15979 /* ------------------------------------------------------------------------- */
15981 static int *loop_sound_frame = NULL;
15982 static int *loop_sound_volume = NULL;
15984 void InitPlayLevelSound()
15986 int num_sounds = getSoundListSize();
15988 checked_free(loop_sound_frame);
15989 checked_free(loop_sound_volume);
15991 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
15992 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15995 static void PlayLevelSound(int x, int y, int nr)
15997 int sx = SCREENX(x), sy = SCREENY(y);
15998 int volume, stereo_position;
15999 int max_distance = 8;
16000 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
16002 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
16003 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
16006 if (!IN_LEV_FIELD(x, y) ||
16007 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
16008 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
16011 volume = SOUND_MAX_VOLUME;
16013 if (!IN_SCR_FIELD(sx, sy))
16015 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
16016 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
16018 volume -= volume * (dx > dy ? dx : dy) / max_distance;
16021 stereo_position = (SOUND_MAX_LEFT +
16022 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
16023 (SCR_FIELDX + 2 * max_distance));
16025 if (IS_LOOP_SOUND(nr))
16027 /* This assures that quieter loop sounds do not overwrite louder ones,
16028 while restarting sound volume comparison with each new game frame. */
16030 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
16033 loop_sound_volume[nr] = volume;
16034 loop_sound_frame[nr] = FrameCounter;
16037 PlaySoundExt(nr, volume, stereo_position, type);
16040 static void PlayLevelSoundNearest(int x, int y, int sound_action)
16042 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
16043 x > LEVELX(BX2) ? LEVELX(BX2) : x,
16044 y < LEVELY(BY1) ? LEVELY(BY1) :
16045 y > LEVELY(BY2) ? LEVELY(BY2) : y,
16049 static void PlayLevelSoundAction(int x, int y, int action)
16051 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
16054 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
16056 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16058 if (sound_effect != SND_UNDEFINED)
16059 PlayLevelSound(x, y, sound_effect);
16062 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
16065 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16067 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16068 PlayLevelSound(x, y, sound_effect);
16071 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
16073 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16075 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16076 PlayLevelSound(x, y, sound_effect);
16079 static void StopLevelSoundActionIfLoop(int x, int y, int action)
16081 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16083 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16084 StopSound(sound_effect);
16087 static void PlayLevelMusic()
16089 if (levelset.music[level_nr] != MUS_UNDEFINED)
16090 PlayMusic(levelset.music[level_nr]); /* from config file */
16092 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
16095 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
16097 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
16098 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
16099 int x = xx - 1 - offset;
16100 int y = yy - 1 - offset;
16105 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
16109 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16113 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16117 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16121 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16125 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16129 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16132 case SAMPLE_android_clone:
16133 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16136 case SAMPLE_android_move:
16137 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16140 case SAMPLE_spring:
16141 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16145 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16149 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16152 case SAMPLE_eater_eat:
16153 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16157 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16160 case SAMPLE_collect:
16161 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16164 case SAMPLE_diamond:
16165 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16168 case SAMPLE_squash:
16169 /* !!! CHECK THIS !!! */
16171 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16173 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16177 case SAMPLE_wonderfall:
16178 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16182 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16186 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16190 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16194 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16198 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16202 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16205 case SAMPLE_wonder:
16206 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16210 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16213 case SAMPLE_exit_open:
16214 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16217 case SAMPLE_exit_leave:
16218 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16221 case SAMPLE_dynamite:
16222 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16226 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16230 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16234 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16238 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16242 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16246 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16250 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16255 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
16257 int element = map_element_SP_to_RND(element_sp);
16258 int action = map_action_SP_to_RND(action_sp);
16259 int offset = (setup.sp_show_border_elements ? 0 : 1);
16260 int x = xx - offset;
16261 int y = yy - offset;
16264 printf("::: %d -> %d\n", element_sp, action_sp);
16267 PlayLevelSoundElementAction(x, y, element, action);
16271 void ChangeTime(int value)
16273 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
16277 /* EMC game engine uses value from time counter of RND game engine */
16278 level.native_em_level->lev->time = *time;
16280 DrawGameValue_Time(*time);
16283 void RaiseScore(int value)
16285 /* EMC game engine and RND game engine have separate score counters */
16286 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
16287 &level.native_em_level->lev->score : &local_player->score);
16291 DrawGameValue_Score(*score);
16295 void RaiseScore(int value)
16297 local_player->score += value;
16300 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16302 DisplayGameControlValues();
16304 DrawGameValue_Score(local_player->score);
16308 void RaiseScoreElement(int element)
16313 case EL_BD_DIAMOND:
16314 case EL_EMERALD_YELLOW:
16315 case EL_EMERALD_RED:
16316 case EL_EMERALD_PURPLE:
16317 case EL_SP_INFOTRON:
16318 RaiseScore(level.score[SC_EMERALD]);
16321 RaiseScore(level.score[SC_DIAMOND]);
16324 RaiseScore(level.score[SC_CRYSTAL]);
16327 RaiseScore(level.score[SC_PEARL]);
16330 case EL_BD_BUTTERFLY:
16331 case EL_SP_ELECTRON:
16332 RaiseScore(level.score[SC_BUG]);
16335 case EL_BD_FIREFLY:
16336 case EL_SP_SNIKSNAK:
16337 RaiseScore(level.score[SC_SPACESHIP]);
16340 case EL_DARK_YAMYAM:
16341 RaiseScore(level.score[SC_YAMYAM]);
16344 RaiseScore(level.score[SC_ROBOT]);
16347 RaiseScore(level.score[SC_PACMAN]);
16350 RaiseScore(level.score[SC_NUT]);
16353 case EL_EM_DYNAMITE:
16354 case EL_SP_DISK_RED:
16355 case EL_DYNABOMB_INCREASE_NUMBER:
16356 case EL_DYNABOMB_INCREASE_SIZE:
16357 case EL_DYNABOMB_INCREASE_POWER:
16358 RaiseScore(level.score[SC_DYNAMITE]);
16360 case EL_SHIELD_NORMAL:
16361 case EL_SHIELD_DEADLY:
16362 RaiseScore(level.score[SC_SHIELD]);
16364 case EL_EXTRA_TIME:
16365 RaiseScore(level.extra_time_score);
16379 case EL_DC_KEY_WHITE:
16380 RaiseScore(level.score[SC_KEY]);
16383 RaiseScore(element_info[element].collect_score);
16388 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16390 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16392 #if defined(NETWORK_AVALIABLE)
16393 if (options.network)
16394 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16403 FadeSkipNextFadeIn();
16405 fading = fading_none;
16409 OpenDoor(DOOR_CLOSE_1);
16412 game_status = GAME_MODE_MAIN;
16415 DrawAndFadeInMainMenu(REDRAW_FIELD);
16423 FadeOut(REDRAW_FIELD);
16426 game_status = GAME_MODE_MAIN;
16428 DrawAndFadeInMainMenu(REDRAW_FIELD);
16432 else /* continue playing the game */
16434 if (tape.playing && tape.deactivate_display)
16435 TapeDeactivateDisplayOff(TRUE);
16437 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16439 if (tape.playing && tape.deactivate_display)
16440 TapeDeactivateDisplayOn();
16444 void RequestQuitGame(boolean ask_if_really_quit)
16446 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16447 boolean skip_request = AllPlayersGone || quick_quit;
16449 RequestQuitGameExt(skip_request, quick_quit,
16450 "Do you really want to quit the game ?");
16454 /* ------------------------------------------------------------------------- */
16455 /* random generator functions */
16456 /* ------------------------------------------------------------------------- */
16458 unsigned int InitEngineRandom_RND(long seed)
16460 game.num_random_calls = 0;
16463 unsigned int rnd_seed = InitEngineRandom(seed);
16465 printf("::: START RND: %d\n", rnd_seed);
16470 return InitEngineRandom(seed);
16476 unsigned int RND(int max)
16480 game.num_random_calls++;
16482 return GetEngineRandom(max);
16489 /* ------------------------------------------------------------------------- */
16490 /* game engine snapshot handling functions */
16491 /* ------------------------------------------------------------------------- */
16493 struct EngineSnapshotInfo
16495 /* runtime values for custom element collect score */
16496 int collect_score[NUM_CUSTOM_ELEMENTS];
16498 /* runtime values for group element choice position */
16499 int choice_pos[NUM_GROUP_ELEMENTS];
16501 /* runtime values for belt position animations */
16502 int belt_graphic[4][NUM_BELT_PARTS];
16503 int belt_anim_mode[4][NUM_BELT_PARTS];
16506 static struct EngineSnapshotInfo engine_snapshot_rnd;
16507 static char *snapshot_level_identifier = NULL;
16508 static int snapshot_level_nr = -1;
16510 static void SaveEngineSnapshotValues_RND()
16512 static int belt_base_active_element[4] =
16514 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16515 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16516 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16517 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16521 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16523 int element = EL_CUSTOM_START + i;
16525 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16528 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16530 int element = EL_GROUP_START + i;
16532 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16535 for (i = 0; i < 4; i++)
16537 for (j = 0; j < NUM_BELT_PARTS; j++)
16539 int element = belt_base_active_element[i] + j;
16540 int graphic = el2img(element);
16541 int anim_mode = graphic_info[graphic].anim_mode;
16543 engine_snapshot_rnd.belt_graphic[i][j] = graphic;
16544 engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
16549 static void LoadEngineSnapshotValues_RND()
16551 unsigned long num_random_calls = game.num_random_calls;
16554 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16556 int element = EL_CUSTOM_START + i;
16558 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16561 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16563 int element = EL_GROUP_START + i;
16565 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16568 for (i = 0; i < 4; i++)
16570 for (j = 0; j < NUM_BELT_PARTS; j++)
16572 int graphic = engine_snapshot_rnd.belt_graphic[i][j];
16573 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
16575 graphic_info[graphic].anim_mode = anim_mode;
16579 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16581 InitRND(tape.random_seed);
16582 for (i = 0; i < num_random_calls; i++)
16586 if (game.num_random_calls != num_random_calls)
16588 Error(ERR_INFO, "number of random calls out of sync");
16589 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16590 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16591 Error(ERR_EXIT, "this should not happen -- please debug");
16595 void SaveEngineSnapshot()
16597 /* do not save snapshots from editor */
16598 if (level_editor_test_game)
16601 /* free previous snapshot buffers, if needed */
16602 FreeEngineSnapshotBuffers();
16604 /* copy some special values to a structure better suited for the snapshot */
16606 SaveEngineSnapshotValues_RND();
16607 SaveEngineSnapshotValues_EM();
16608 SaveEngineSnapshotValues_SP();
16610 /* save values stored in special snapshot structure */
16612 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16613 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16614 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16616 /* save further RND engine values */
16618 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16619 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16620 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16622 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16623 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16624 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16625 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16627 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16628 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16629 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16630 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16631 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16633 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16634 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16635 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16637 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16639 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16641 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16642 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16644 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16645 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16646 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16647 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16648 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16649 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16650 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16651 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16652 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16653 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16654 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16655 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16656 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16657 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16658 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16659 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16660 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16661 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16663 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16664 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16666 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16667 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16668 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16670 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16671 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16673 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16674 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16675 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16676 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16677 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16679 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16680 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16682 /* save level identification information */
16684 setString(&snapshot_level_identifier, leveldir_current->identifier);
16685 snapshot_level_nr = level_nr;
16688 ListNode *node = engine_snapshot_list_rnd;
16691 while (node != NULL)
16693 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16698 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16702 void LoadEngineSnapshot()
16704 /* restore generically stored snapshot buffers */
16706 LoadEngineSnapshotBuffers();
16708 /* restore special values from snapshot structure */
16710 LoadEngineSnapshotValues_RND();
16711 LoadEngineSnapshotValues_EM();
16712 LoadEngineSnapshotValues_SP();
16715 boolean CheckEngineSnapshot()
16717 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16718 snapshot_level_nr == level_nr);
16722 /* ---------- new game button stuff ---------------------------------------- */
16730 } gamebutton_info[NUM_GAME_BUTTONS] =
16733 IMG_GAME_BUTTON_GFX_STOP, &game.button.stop,
16734 GAME_CTRL_ID_STOP, "stop game"
16737 IMG_GAME_BUTTON_GFX_PAUSE, &game.button.pause,
16738 GAME_CTRL_ID_PAUSE, "pause game"
16741 IMG_GAME_BUTTON_GFX_PLAY, &game.button.play,
16742 GAME_CTRL_ID_PLAY, "play game"
16745 IMG_GAME_BUTTON_GFX_SOUND_MUSIC, &game.button.sound_music,
16746 SOUND_CTRL_ID_MUSIC, "background music on/off"
16749 IMG_GAME_BUTTON_GFX_SOUND_LOOPS, &game.button.sound_loops,
16750 SOUND_CTRL_ID_LOOPS, "sound loops on/off"
16753 IMG_GAME_BUTTON_GFX_SOUND_SIMPLE, &game.button.sound_simple,
16754 SOUND_CTRL_ID_SIMPLE, "normal sounds on/off"
16758 void CreateGameButtons()
16762 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16764 struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
16765 struct Rect *pos = gamebutton_info[i].pos;
16766 struct GadgetInfo *gi;
16769 unsigned long event_mask;
16770 int gd_x = gfx->src_x;
16771 int gd_y = gfx->src_y;
16772 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
16773 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
16774 int gd_xa = gfx->src_x + gfx->active_xoffset;
16775 int gd_ya = gfx->src_y + gfx->active_yoffset;
16776 int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16777 int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16780 if (id == GAME_CTRL_ID_STOP ||
16781 id == GAME_CTRL_ID_PAUSE ||
16782 id == GAME_CTRL_ID_PLAY)
16784 button_type = GD_TYPE_NORMAL_BUTTON;
16786 event_mask = GD_EVENT_RELEASED;
16790 button_type = GD_TYPE_CHECK_BUTTON;
16792 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16793 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16794 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16795 event_mask = GD_EVENT_PRESSED;
16798 gi = CreateGadget(GDI_CUSTOM_ID, id,
16799 GDI_INFO_TEXT, gamebutton_info[i].infotext,
16800 GDI_X, DX + pos->x,
16801 GDI_Y, DY + pos->y,
16802 GDI_WIDTH, gfx->width,
16803 GDI_HEIGHT, gfx->height,
16804 GDI_TYPE, button_type,
16805 GDI_STATE, GD_BUTTON_UNPRESSED,
16806 GDI_CHECKED, checked,
16807 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16808 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16809 GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16810 GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16811 GDI_DIRECT_DRAW, FALSE,
16812 GDI_EVENT_MASK, event_mask,
16813 GDI_CALLBACK_ACTION, HandleGameButtons,
16817 Error(ERR_EXIT, "cannot create gadget");
16819 game_gadget[id] = gi;
16823 void FreeGameButtons()
16827 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16828 FreeGadget(game_gadget[i]);
16831 static void MapGameButtons()
16835 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16836 MapGadget(game_gadget[i]);
16839 void UnmapGameButtons()
16843 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16844 UnmapGadget(game_gadget[i]);
16847 void RedrawGameButtons()
16851 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16852 RedrawGadget(game_gadget[i]);
16855 static void HandleGameButtonsExt(int id)
16857 if (game_status != GAME_MODE_PLAYING)
16862 case GAME_CTRL_ID_STOP:
16866 RequestQuitGame(TRUE);
16869 case GAME_CTRL_ID_PAUSE:
16870 if (options.network)
16872 #if defined(NETWORK_AVALIABLE)
16874 SendToServer_ContinuePlaying();
16876 SendToServer_PausePlaying();
16880 TapeTogglePause(TAPE_TOGGLE_MANUAL);
16883 case GAME_CTRL_ID_PLAY:
16886 #if defined(NETWORK_AVALIABLE)
16887 if (options.network)
16888 SendToServer_ContinuePlaying();
16892 tape.pausing = FALSE;
16893 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16898 case SOUND_CTRL_ID_MUSIC:
16899 if (setup.sound_music)
16901 setup.sound_music = FALSE;
16905 else if (audio.music_available)
16907 setup.sound = setup.sound_music = TRUE;
16909 SetAudioMode(setup.sound);
16915 case SOUND_CTRL_ID_LOOPS:
16916 if (setup.sound_loops)
16917 setup.sound_loops = FALSE;
16918 else if (audio.loops_available)
16920 setup.sound = setup.sound_loops = TRUE;
16922 SetAudioMode(setup.sound);
16926 case SOUND_CTRL_ID_SIMPLE:
16927 if (setup.sound_simple)
16928 setup.sound_simple = FALSE;
16929 else if (audio.sound_available)
16931 setup.sound = setup.sound_simple = TRUE;
16933 SetAudioMode(setup.sound);
16942 static void HandleGameButtons(struct GadgetInfo *gi)
16944 HandleGameButtonsExt(gi->custom_id);
16947 void HandleSoundButtonKeys(Key key)
16950 if (key == setup.shortcut.sound_simple)
16951 ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16952 else if (key == setup.shortcut.sound_loops)
16953 ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16954 else if (key == setup.shortcut.sound_music)
16955 ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16957 if (key == setup.shortcut.sound_simple)
16958 HandleGameButtonsExt(SOUND_CTRL_ID_SIMPLE);
16959 else if (key == setup.shortcut.sound_loops)
16960 HandleGameButtonsExt(SOUND_CTRL_ID_LOOPS);
16961 else if (key == setup.shortcut.sound_music)
16962 HandleGameButtonsExt(SOUND_CTRL_ID_MUSIC);