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()
2227 int time = (local_player->LevelSolved ?
2228 local_player->LevelSolved_CountingTime :
2229 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2230 level.native_em_level->lev->time :
2231 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2232 level.native_sp_level->game_sp->time_played :
2233 game.no_time_limit ? TimePlayed : TimeLeft);
2235 int time = (local_player->LevelSolved ?
2236 local_player->LevelSolved_CountingTime :
2237 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2238 level.native_em_level->lev->time :
2239 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2240 level.native_sp_level->game_sp->time_played :
2241 level.time == 0 ? TimePlayed : TimeLeft);
2243 int score = (local_player->LevelSolved ?
2244 local_player->LevelSolved_CountingScore :
2245 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2246 level.native_em_level->lev->score :
2247 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2248 level.native_sp_level->game_sp->score :
2249 local_player->score);
2250 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2251 level.native_em_level->lev->required :
2252 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2253 level.native_sp_level->game_sp->infotrons_still_needed :
2254 local_player->gems_still_needed);
2255 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2256 level.native_em_level->lev->required > 0 :
2257 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2258 level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2259 local_player->gems_still_needed > 0 ||
2260 local_player->sokobanfields_still_needed > 0 ||
2261 local_player->lights_still_needed > 0);
2263 UpdatePlayfieldElementCount();
2265 /* update game panel control values */
2267 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2268 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2270 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2271 for (i = 0; i < MAX_NUM_KEYS; i++)
2272 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2273 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2274 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2276 if (game.centered_player_nr == -1)
2278 for (i = 0; i < MAX_PLAYERS; i++)
2280 /* only one player in Supaplex game engine */
2281 if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2284 for (k = 0; k < MAX_NUM_KEYS; k++)
2286 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2288 if (level.native_em_level->ply[i]->keys & (1 << k))
2289 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2290 get_key_element_from_nr(k);
2292 else if (stored_player[i].key[k])
2293 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2294 get_key_element_from_nr(k);
2297 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2298 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2299 level.native_em_level->ply[i]->dynamite;
2300 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2301 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2302 level.native_sp_level->game_sp->red_disk_count;
2304 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2305 stored_player[i].inventory_size;
2307 if (stored_player[i].num_white_keys > 0)
2308 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2311 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2312 stored_player[i].num_white_keys;
2317 int player_nr = game.centered_player_nr;
2319 for (k = 0; k < MAX_NUM_KEYS; k++)
2321 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2323 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2324 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2325 get_key_element_from_nr(k);
2327 else if (stored_player[player_nr].key[k])
2328 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2329 get_key_element_from_nr(k);
2332 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2333 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2334 level.native_em_level->ply[player_nr]->dynamite;
2335 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2336 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2337 level.native_sp_level->game_sp->red_disk_count;
2339 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2340 stored_player[player_nr].inventory_size;
2342 if (stored_player[player_nr].num_white_keys > 0)
2343 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2345 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2346 stored_player[player_nr].num_white_keys;
2349 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2351 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2352 get_inventory_element_from_pos(local_player, i);
2353 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2354 get_inventory_element_from_pos(local_player, -i - 1);
2357 game_panel_controls[GAME_PANEL_SCORE].value = score;
2358 game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2360 game_panel_controls[GAME_PANEL_TIME].value = time;
2362 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2363 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2364 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2366 game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2368 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2369 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2371 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2372 local_player->shield_normal_time_left;
2373 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2374 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2376 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2377 local_player->shield_deadly_time_left;
2379 game_panel_controls[GAME_PANEL_EXIT].value =
2380 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2382 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2383 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2384 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2385 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2386 EL_EMC_MAGIC_BALL_SWITCH);
2388 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2389 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2390 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2391 game.light_time_left;
2393 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2394 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2395 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2396 game.timegate_time_left;
2398 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2399 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2401 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2402 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2403 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2404 game.lenses_time_left;
2406 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2407 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2408 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2409 game.magnify_time_left;
2411 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2412 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2413 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2414 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2415 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2416 EL_BALLOON_SWITCH_NONE);
2418 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2419 local_player->dynabomb_count;
2420 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2421 local_player->dynabomb_size;
2422 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2423 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2425 game_panel_controls[GAME_PANEL_PENGUINS].value =
2426 local_player->friends_still_needed;
2428 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2429 local_player->sokobanfields_still_needed;
2430 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2431 local_player->sokobanfields_still_needed;
2433 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2434 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2436 for (i = 0; i < NUM_BELTS; i++)
2438 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2439 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2440 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2441 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2442 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2445 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2446 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2447 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2448 game.magic_wall_time_left;
2450 #if USE_PLAYER_GRAVITY
2451 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2452 local_player->gravity;
2454 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2457 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2458 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2460 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2461 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2462 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2463 game.panel.element[i].id : EL_UNDEFINED);
2465 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2466 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2467 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2468 element_info[game.panel.element_count[i].id].element_count : 0);
2470 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2471 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2472 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2473 element_info[game.panel.ce_score[i].id].collect_score : 0);
2475 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2476 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2477 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2478 element_info[game.panel.ce_score_element[i].id].collect_score :
2481 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2482 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2483 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2485 /* update game panel control frames */
2487 for (i = 0; game_panel_controls[i].nr != -1; i++)
2489 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2491 if (gpc->type == TYPE_ELEMENT)
2493 if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2495 int last_anim_random_frame = gfx.anim_random_frame;
2496 int element = gpc->value;
2497 int graphic = el2panelimg(element);
2499 if (gpc->value != gpc->last_value)
2502 gpc->gfx_random = INIT_GFX_RANDOM();
2508 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2509 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2510 gpc->gfx_random = INIT_GFX_RANDOM();
2513 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2514 gfx.anim_random_frame = gpc->gfx_random;
2516 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2517 gpc->gfx_frame = element_info[element].collect_score;
2519 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2522 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2523 gfx.anim_random_frame = last_anim_random_frame;
2529 void DisplayGameControlValues()
2531 boolean redraw_panel = FALSE;
2534 for (i = 0; game_panel_controls[i].nr != -1; i++)
2536 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2538 if (PANEL_DEACTIVATED(gpc->pos))
2541 if (gpc->value == gpc->last_value &&
2542 gpc->frame == gpc->last_frame)
2545 redraw_panel = TRUE;
2551 /* copy default game door content to main double buffer */
2553 /* !!! CHECK AGAIN !!! */
2554 SetPanelBackground();
2555 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2556 DrawBackground(DX, DY, DXSIZE, DYSIZE);
2558 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2559 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2562 /* redraw game control buttons */
2564 RedrawGameButtons();
2570 game_status = GAME_MODE_PSEUDO_PANEL;
2573 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2575 for (i = 0; game_panel_controls[i].nr != -1; i++)
2579 int nr = game_panel_order[i].nr;
2580 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2582 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2585 struct TextPosInfo *pos = gpc->pos;
2586 int type = gpc->type;
2587 int value = gpc->value;
2588 int frame = gpc->frame;
2590 int last_value = gpc->last_value;
2591 int last_frame = gpc->last_frame;
2593 int size = pos->size;
2594 int font = pos->font;
2595 boolean draw_masked = pos->draw_masked;
2596 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2598 if (PANEL_DEACTIVATED(pos))
2602 if (value == last_value && frame == last_frame)
2606 gpc->last_value = value;
2607 gpc->last_frame = frame;
2610 printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2613 if (type == TYPE_INTEGER)
2615 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2616 nr == GAME_PANEL_TIME)
2618 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2620 if (use_dynamic_size) /* use dynamic number of digits */
2622 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2623 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2624 int size2 = size1 + 1;
2625 int font1 = pos->font;
2626 int font2 = pos->font_alt;
2628 size = (value < value_change ? size1 : size2);
2629 font = (value < value_change ? font1 : font2);
2632 /* clear background if value just changed its size (dynamic digits) */
2633 if ((last_value < value_change) != (value < value_change))
2635 int width1 = size1 * getFontWidth(font1);
2636 int width2 = size2 * getFontWidth(font2);
2637 int max_width = MAX(width1, width2);
2638 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2640 pos->width = max_width;
2642 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2643 max_width, max_height);
2650 /* correct text size if "digits" is zero or less */
2652 size = strlen(int2str(value, size));
2654 /* dynamically correct text alignment */
2655 pos->width = size * getFontWidth(font);
2658 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2659 int2str(value, size), font, mask_mode);
2661 else if (type == TYPE_ELEMENT)
2663 int element, graphic;
2667 int dst_x = PANEL_XPOS(pos);
2668 int dst_y = PANEL_YPOS(pos);
2671 if (value != EL_UNDEFINED && value != EL_EMPTY)
2674 graphic = el2panelimg(value);
2676 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2679 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2683 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2686 width = graphic_info[graphic].width * size / TILESIZE;
2687 height = graphic_info[graphic].height * size / TILESIZE;
2691 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2692 dst_x - src_x, dst_y - src_y);
2693 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2698 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2703 if (value == EL_UNDEFINED || value == EL_EMPTY)
2705 element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2706 graphic = el2panelimg(element);
2708 src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2709 src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2710 src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2715 graphic = el2panelimg(value);
2717 getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2720 width = graphic_info[graphic].width * size / TILESIZE;
2721 height = graphic_info[graphic].height * size / TILESIZE;
2723 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2726 else if (type == TYPE_STRING)
2728 boolean active = (value != 0);
2729 char *state_normal = "off";
2730 char *state_active = "on";
2731 char *state = (active ? state_active : state_normal);
2732 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2733 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2734 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2735 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2737 if (nr == GAME_PANEL_GRAVITY_STATE)
2739 int font1 = pos->font; /* (used for normal state) */
2740 int font2 = pos->font_alt; /* (used for active state) */
2742 int size1 = strlen(state_normal);
2743 int size2 = strlen(state_active);
2744 int width1 = size1 * getFontWidth(font1);
2745 int width2 = size2 * getFontWidth(font2);
2746 int max_width = MAX(width1, width2);
2747 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2749 pos->width = max_width;
2751 /* clear background for values that may have changed its size */
2752 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2753 max_width, max_height);
2756 font = (active ? font2 : font1);
2766 /* don't truncate output if "chars" is zero or less */
2769 /* dynamically correct text alignment */
2770 pos->width = size * getFontWidth(font);
2774 s_cut = getStringCopyN(s, size);
2776 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2777 s_cut, font, mask_mode);
2783 redraw_mask |= REDRAW_DOOR_1;
2786 game_status = GAME_MODE_PLAYING;
2789 void UpdateAndDisplayGameControlValues()
2791 if (tape.warp_forward)
2794 UpdateGameControlValues();
2795 DisplayGameControlValues();
2798 void DrawGameValue_Emeralds(int value)
2800 struct TextPosInfo *pos = &game.panel.gems;
2802 int font_nr = pos->font;
2804 int font_nr = FONT_TEXT_2;
2806 int font_width = getFontWidth(font_nr);
2807 int chars = pos->size;
2810 return; /* !!! USE NEW STUFF !!! */
2813 if (PANEL_DEACTIVATED(pos))
2816 pos->width = chars * font_width;
2818 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2821 void DrawGameValue_Dynamite(int value)
2823 struct TextPosInfo *pos = &game.panel.inventory_count;
2825 int font_nr = pos->font;
2827 int font_nr = FONT_TEXT_2;
2829 int font_width = getFontWidth(font_nr);
2830 int chars = pos->size;
2833 return; /* !!! USE NEW STUFF !!! */
2836 if (PANEL_DEACTIVATED(pos))
2839 pos->width = chars * font_width;
2841 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2844 void DrawGameValue_Score(int value)
2846 struct TextPosInfo *pos = &game.panel.score;
2848 int font_nr = pos->font;
2850 int font_nr = FONT_TEXT_2;
2852 int font_width = getFontWidth(font_nr);
2853 int chars = pos->size;
2856 return; /* !!! USE NEW STUFF !!! */
2859 if (PANEL_DEACTIVATED(pos))
2862 pos->width = chars * font_width;
2864 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2867 void DrawGameValue_Time(int value)
2869 struct TextPosInfo *pos = &game.panel.time;
2870 static int last_value = -1;
2873 int chars = pos->size;
2875 int font1_nr = pos->font;
2876 int font2_nr = pos->font_alt;
2878 int font1_nr = FONT_TEXT_2;
2879 int font2_nr = FONT_TEXT_1;
2881 int font_nr = font1_nr;
2882 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2885 return; /* !!! USE NEW STUFF !!! */
2888 if (PANEL_DEACTIVATED(pos))
2891 if (use_dynamic_chars) /* use dynamic number of chars */
2893 chars = (value < 1000 ? chars1 : chars2);
2894 font_nr = (value < 1000 ? font1_nr : font2_nr);
2897 /* clear background if value just changed its size (dynamic chars only) */
2898 if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2900 int width1 = chars1 * getFontWidth(font1_nr);
2901 int width2 = chars2 * getFontWidth(font2_nr);
2902 int max_width = MAX(width1, width2);
2903 int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2905 pos->width = max_width;
2907 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2908 max_width, max_height);
2911 pos->width = chars * getFontWidth(font_nr);
2913 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2918 void DrawGameValue_Level(int value)
2920 struct TextPosInfo *pos = &game.panel.level_number;
2923 int chars = pos->size;
2925 int font1_nr = pos->font;
2926 int font2_nr = pos->font_alt;
2928 int font1_nr = FONT_TEXT_2;
2929 int font2_nr = FONT_TEXT_1;
2931 int font_nr = font1_nr;
2932 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2935 return; /* !!! USE NEW STUFF !!! */
2938 if (PANEL_DEACTIVATED(pos))
2941 if (use_dynamic_chars) /* use dynamic number of chars */
2943 chars = (level_nr < 100 ? chars1 : chars2);
2944 font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2947 pos->width = chars * getFontWidth(font_nr);
2949 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2952 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2955 struct TextPosInfo *pos = &game.panel.keys;
2958 int base_key_graphic = EL_KEY_1;
2963 return; /* !!! USE NEW STUFF !!! */
2967 if (PANEL_DEACTIVATED(pos))
2972 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2973 base_key_graphic = EL_EM_KEY_1;
2977 pos->width = 4 * MINI_TILEX;
2981 for (i = 0; i < MAX_NUM_KEYS; i++)
2983 /* currently only 4 of 8 possible keys are displayed */
2984 for (i = 0; i < STD_NUM_KEYS; i++)
2988 struct TextPosInfo *pos = &game.panel.key[i];
2990 int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2991 int src_y = DOOR_GFX_PAGEY1 + 123;
2993 int dst_x = PANEL_XPOS(pos);
2994 int dst_y = PANEL_YPOS(pos);
2996 int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2997 int dst_y = PANEL_YPOS(pos);
3001 int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
3002 level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
3004 int graphic = el2edimg(element);
3008 if (PANEL_DEACTIVATED(pos))
3013 /* masked blit with tiles from half-size scaled bitmap does not work yet
3014 (no mask bitmap created for these sizes after loading and scaling) --
3015 solution: load without creating mask, scale, then create final mask */
3017 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3018 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3023 int graphic = el2edimg(base_key_graphic + i);
3028 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
3030 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
3031 dst_x - src_x, dst_y - src_y);
3032 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
3038 DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
3040 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3041 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3044 DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
3046 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3047 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3055 void DrawGameValue_Emeralds(int value)
3057 int font_nr = FONT_TEXT_2;
3058 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3060 if (PANEL_DEACTIVATED(game.panel.gems))
3063 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
3066 void DrawGameValue_Dynamite(int value)
3068 int font_nr = FONT_TEXT_2;
3069 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3071 if (PANEL_DEACTIVATED(game.panel.inventory_count))
3074 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
3077 void DrawGameValue_Score(int value)
3079 int font_nr = FONT_TEXT_2;
3080 int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
3082 if (PANEL_DEACTIVATED(game.panel.score))
3085 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
3088 void DrawGameValue_Time(int value)
3090 int font1_nr = FONT_TEXT_2;
3092 int font2_nr = FONT_TEXT_1;
3094 int font2_nr = FONT_LEVEL_NUMBER;
3096 int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
3097 int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
3099 if (PANEL_DEACTIVATED(game.panel.time))
3102 /* clear background if value just changed its size */
3103 if (value == 999 || value == 1000)
3104 ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
3107 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
3109 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
3112 void DrawGameValue_Level(int value)
3114 int font1_nr = FONT_TEXT_2;
3116 int font2_nr = FONT_TEXT_1;
3118 int font2_nr = FONT_LEVEL_NUMBER;
3121 if (PANEL_DEACTIVATED(game.panel.level))
3125 DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
3127 DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
3130 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
3132 int base_key_graphic = EL_KEY_1;
3135 if (PANEL_DEACTIVATED(game.panel.keys))
3138 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3139 base_key_graphic = EL_EM_KEY_1;
3141 /* currently only 4 of 8 possible keys are displayed */
3142 for (i = 0; i < STD_NUM_KEYS; i++)
3144 int x = XX_KEYS + i * MINI_TILEX;
3148 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3150 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3151 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3157 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3160 int key[MAX_NUM_KEYS];
3163 /* prevent EM engine from updating time/score values parallel to GameWon() */
3164 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3165 local_player->LevelSolved)
3168 for (i = 0; i < MAX_NUM_KEYS; i++)
3169 key[i] = key_bits & (1 << i);
3171 DrawGameValue_Level(level_nr);
3173 DrawGameValue_Emeralds(emeralds);
3174 DrawGameValue_Dynamite(dynamite);
3175 DrawGameValue_Score(score);
3176 DrawGameValue_Time(time);
3178 DrawGameValue_Keys(key);
3181 void UpdateGameDoorValues()
3183 UpdateGameControlValues();
3186 void DrawGameDoorValues()
3188 DisplayGameControlValues();
3191 void DrawGameDoorValues_OLD()
3193 int time_value = (game.no_time_limit ? TimePlayed : TimeLeft);
3194 int dynamite_value = 0;
3195 int score_value = (local_player->LevelSolved ? local_player->score_final :
3196 local_player->score);
3197 int gems_value = local_player->gems_still_needed;
3201 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3203 DrawGameDoorValues_EM();
3208 if (game.centered_player_nr == -1)
3210 for (i = 0; i < MAX_PLAYERS; i++)
3212 for (j = 0; j < MAX_NUM_KEYS; j++)
3213 if (stored_player[i].key[j])
3214 key_bits |= (1 << j);
3216 dynamite_value += stored_player[i].inventory_size;
3221 int player_nr = game.centered_player_nr;
3223 for (i = 0; i < MAX_NUM_KEYS; i++)
3224 if (stored_player[player_nr].key[i])
3225 key_bits |= (1 << i);
3227 dynamite_value = stored_player[player_nr].inventory_size;
3230 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3236 =============================================================================
3238 -----------------------------------------------------------------------------
3239 initialize game engine due to level / tape version number
3240 =============================================================================
3243 static void InitGameEngine()
3245 int i, j, k, l, x, y;
3247 /* set game engine from tape file when re-playing, else from level file */
3248 game.engine_version = (tape.playing ? tape.engine_version :
3249 level.game_version);
3251 /* ---------------------------------------------------------------------- */
3252 /* set flags for bugs and changes according to active game engine version */
3253 /* ---------------------------------------------------------------------- */
3256 Summary of bugfix/change:
3257 Fixed handling for custom elements that change when pushed by the player.
3259 Fixed/changed in version:
3263 Before 3.1.0, custom elements that "change when pushing" changed directly
3264 after the player started pushing them (until then handled in "DigField()").
3265 Since 3.1.0, these custom elements are not changed until the "pushing"
3266 move of the element is finished (now handled in "ContinueMoving()").
3268 Affected levels/tapes:
3269 The first condition is generally needed for all levels/tapes before version
3270 3.1.0, which might use the old behaviour before it was changed; known tapes
3271 that are affected are some tapes from the level set "Walpurgis Gardens" by
3273 The second condition is an exception from the above case and is needed for
3274 the special case of tapes recorded with game (not engine!) version 3.1.0 or
3275 above (including some development versions of 3.1.0), but before it was
3276 known that this change would break tapes like the above and was fixed in
3277 3.1.1, so that the changed behaviour was active although the engine version
3278 while recording maybe was before 3.1.0. There is at least one tape that is
3279 affected by this exception, which is the tape for the one-level set "Bug
3280 Machine" by Juergen Bonhagen.
3283 game.use_change_when_pushing_bug =
3284 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3286 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3287 tape.game_version < VERSION_IDENT(3,1,1,0)));
3290 Summary of bugfix/change:
3291 Fixed handling for blocking the field the player leaves when moving.
3293 Fixed/changed in version:
3297 Before 3.1.1, when "block last field when moving" was enabled, the field
3298 the player is leaving when moving was blocked for the time of the move,
3299 and was directly unblocked afterwards. This resulted in the last field
3300 being blocked for exactly one less than the number of frames of one player
3301 move. Additionally, even when blocking was disabled, the last field was
3302 blocked for exactly one frame.
3303 Since 3.1.1, due to changes in player movement handling, the last field
3304 is not blocked at all when blocking is disabled. When blocking is enabled,
3305 the last field is blocked for exactly the number of frames of one player
3306 move. Additionally, if the player is Murphy, the hero of Supaplex, the
3307 last field is blocked for exactly one more than the number of frames of
3310 Affected levels/tapes:
3311 (!!! yet to be determined -- probably many !!!)
3314 game.use_block_last_field_bug =
3315 (game.engine_version < VERSION_IDENT(3,1,1,0));
3318 Summary of bugfix/change:
3319 Changed behaviour of CE changes with multiple changes per single frame.
3321 Fixed/changed in version:
3325 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3326 This resulted in race conditions where CEs seem to behave strange in some
3327 situations (where triggered CE changes were just skipped because there was
3328 already a CE change on that tile in the playfield in that engine frame).
3329 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3330 (The number of changes per frame must be limited in any case, because else
3331 it is easily possible to define CE changes that would result in an infinite
3332 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3333 should be set large enough so that it would only be reached in cases where
3334 the corresponding CE change conditions run into a loop. Therefore, it seems
3335 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3336 maximal number of change pages for custom elements.)
3338 Affected levels/tapes:
3342 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3343 game.max_num_changes_per_frame = 1;
3345 game.max_num_changes_per_frame =
3346 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3349 /* ---------------------------------------------------------------------- */
3351 /* default scan direction: scan playfield from top/left to bottom/right */
3352 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3354 /* dynamically adjust element properties according to game engine version */
3355 InitElementPropertiesEngine(game.engine_version);
3358 printf("level %d: level version == %06d\n", level_nr, level.game_version);
3359 printf(" tape version == %06d [%s] [file: %06d]\n",
3360 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3362 printf(" => game.engine_version == %06d\n", game.engine_version);
3365 /* ---------- initialize player's initial move delay --------------------- */
3367 /* dynamically adjust player properties according to level information */
3368 for (i = 0; i < MAX_PLAYERS; i++)
3369 game.initial_move_delay_value[i] =
3370 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3372 /* dynamically adjust player properties according to game engine version */
3373 for (i = 0; i < MAX_PLAYERS; i++)
3374 game.initial_move_delay[i] =
3375 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3376 game.initial_move_delay_value[i] : 0);
3378 /* ---------- initialize player's initial push delay --------------------- */
3380 /* dynamically adjust player properties according to game engine version */
3381 game.initial_push_delay_value =
3382 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3384 /* ---------- initialize changing elements ------------------------------- */
3386 /* initialize changing elements information */
3387 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3389 struct ElementInfo *ei = &element_info[i];
3391 /* this pointer might have been changed in the level editor */
3392 ei->change = &ei->change_page[0];
3394 if (!IS_CUSTOM_ELEMENT(i))
3396 ei->change->target_element = EL_EMPTY_SPACE;
3397 ei->change->delay_fixed = 0;
3398 ei->change->delay_random = 0;
3399 ei->change->delay_frames = 1;
3402 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3404 ei->has_change_event[j] = FALSE;
3406 ei->event_page_nr[j] = 0;
3407 ei->event_page[j] = &ei->change_page[0];
3411 /* add changing elements from pre-defined list */
3412 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3414 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3415 struct ElementInfo *ei = &element_info[ch_delay->element];
3417 ei->change->target_element = ch_delay->target_element;
3418 ei->change->delay_fixed = ch_delay->change_delay;
3420 ei->change->pre_change_function = ch_delay->pre_change_function;
3421 ei->change->change_function = ch_delay->change_function;
3422 ei->change->post_change_function = ch_delay->post_change_function;
3424 ei->change->can_change = TRUE;
3425 ei->change->can_change_or_has_action = TRUE;
3427 ei->has_change_event[CE_DELAY] = TRUE;
3429 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3430 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3433 /* ---------- initialize internal run-time variables --------------------- */
3435 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3437 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3439 for (j = 0; j < ei->num_change_pages; j++)
3441 ei->change_page[j].can_change_or_has_action =
3442 (ei->change_page[j].can_change |
3443 ei->change_page[j].has_action);
3447 /* add change events from custom element configuration */
3448 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3450 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3452 for (j = 0; j < ei->num_change_pages; j++)
3454 if (!ei->change_page[j].can_change_or_has_action)
3457 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3459 /* only add event page for the first page found with this event */
3460 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3462 ei->has_change_event[k] = TRUE;
3464 ei->event_page_nr[k] = j;
3465 ei->event_page[k] = &ei->change_page[j];
3472 /* ---------- initialize reference elements in change conditions --------- */
3474 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3476 int element = EL_CUSTOM_START + i;
3477 struct ElementInfo *ei = &element_info[element];
3479 for (j = 0; j < ei->num_change_pages; j++)
3481 int trigger_element = ei->change_page[j].initial_trigger_element;
3483 if (trigger_element >= EL_PREV_CE_8 &&
3484 trigger_element <= EL_NEXT_CE_8)
3485 trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3487 ei->change_page[j].trigger_element = trigger_element;
3492 /* ---------- initialize run-time trigger player and element ------------- */
3494 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3496 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3498 for (j = 0; j < ei->num_change_pages; j++)
3500 ei->change_page[j].actual_trigger_element = EL_EMPTY;
3501 ei->change_page[j].actual_trigger_player = EL_EMPTY;
3502 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3503 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3504 ei->change_page[j].actual_trigger_ce_value = 0;
3505 ei->change_page[j].actual_trigger_ce_score = 0;
3509 /* ---------- initialize trigger events ---------------------------------- */
3511 /* initialize trigger events information */
3512 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3513 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3514 trigger_events[i][j] = FALSE;
3516 /* add trigger events from element change event properties */
3517 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3519 struct ElementInfo *ei = &element_info[i];
3521 for (j = 0; j < ei->num_change_pages; j++)
3523 if (!ei->change_page[j].can_change_or_has_action)
3526 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3528 int trigger_element = ei->change_page[j].trigger_element;
3530 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3532 if (ei->change_page[j].has_event[k])
3534 if (IS_GROUP_ELEMENT(trigger_element))
3536 struct ElementGroupInfo *group =
3537 element_info[trigger_element].group;
3539 for (l = 0; l < group->num_elements_resolved; l++)
3540 trigger_events[group->element_resolved[l]][k] = TRUE;
3542 else if (trigger_element == EL_ANY_ELEMENT)
3543 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3544 trigger_events[l][k] = TRUE;
3546 trigger_events[trigger_element][k] = TRUE;
3553 /* ---------- initialize push delay -------------------------------------- */
3555 /* initialize push delay values to default */
3556 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3558 if (!IS_CUSTOM_ELEMENT(i))
3560 /* set default push delay values (corrected since version 3.0.7-1) */
3561 if (game.engine_version < VERSION_IDENT(3,0,7,1))
3563 element_info[i].push_delay_fixed = 2;
3564 element_info[i].push_delay_random = 8;
3568 element_info[i].push_delay_fixed = 8;
3569 element_info[i].push_delay_random = 8;
3574 /* set push delay value for certain elements from pre-defined list */
3575 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3577 int e = push_delay_list[i].element;
3579 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
3580 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3583 /* set push delay value for Supaplex elements for newer engine versions */
3584 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3586 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3588 if (IS_SP_ELEMENT(i))
3590 /* set SP push delay to just enough to push under a falling zonk */
3591 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3593 element_info[i].push_delay_fixed = delay;
3594 element_info[i].push_delay_random = 0;
3599 /* ---------- initialize move stepsize ----------------------------------- */
3601 /* initialize move stepsize values to default */
3602 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3603 if (!IS_CUSTOM_ELEMENT(i))
3604 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3606 /* set move stepsize value for certain elements from pre-defined list */
3607 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3609 int e = move_stepsize_list[i].element;
3611 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3614 /* ---------- initialize collect score ----------------------------------- */
3616 /* initialize collect score values for custom elements from initial value */
3617 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3618 if (IS_CUSTOM_ELEMENT(i))
3619 element_info[i].collect_score = element_info[i].collect_score_initial;
3621 /* ---------- initialize collect count ----------------------------------- */
3623 /* initialize collect count values for non-custom elements */
3624 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3625 if (!IS_CUSTOM_ELEMENT(i))
3626 element_info[i].collect_count_initial = 0;
3628 /* add collect count values for all elements from pre-defined list */
3629 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3630 element_info[collect_count_list[i].element].collect_count_initial =
3631 collect_count_list[i].count;
3633 /* ---------- initialize access direction -------------------------------- */
3635 /* initialize access direction values to default (access from every side) */
3636 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3637 if (!IS_CUSTOM_ELEMENT(i))
3638 element_info[i].access_direction = MV_ALL_DIRECTIONS;
3640 /* set access direction value for certain elements from pre-defined list */
3641 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3642 element_info[access_direction_list[i].element].access_direction =
3643 access_direction_list[i].direction;
3645 /* ---------- initialize explosion content ------------------------------- */
3646 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3648 if (IS_CUSTOM_ELEMENT(i))
3651 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3653 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3655 element_info[i].content.e[x][y] =
3656 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3657 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3658 i == EL_PLAYER_3 ? EL_EMERALD :
3659 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3660 i == EL_MOLE ? EL_EMERALD_RED :
3661 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3662 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3663 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3664 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3665 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3666 i == EL_WALL_EMERALD ? EL_EMERALD :
3667 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3668 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3669 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3670 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3671 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3672 i == EL_WALL_PEARL ? EL_PEARL :
3673 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3678 /* ---------- initialize recursion detection ------------------------------ */
3679 recursion_loop_depth = 0;
3680 recursion_loop_detected = FALSE;
3681 recursion_loop_element = EL_UNDEFINED;
3683 /* ---------- initialize graphics engine ---------------------------------- */
3684 game.scroll_delay_value =
3685 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3686 setup.scroll_delay ? setup.scroll_delay_value : 0);
3687 game.scroll_delay_value =
3688 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3691 int get_num_special_action(int element, int action_first, int action_last)
3693 int num_special_action = 0;
3696 for (i = action_first; i <= action_last; i++)
3698 boolean found = FALSE;
3700 for (j = 0; j < NUM_DIRECTIONS; j++)
3701 if (el_act_dir2img(element, i, j) !=
3702 el_act_dir2img(element, ACTION_DEFAULT, j))
3706 num_special_action++;
3711 return num_special_action;
3716 =============================================================================
3718 -----------------------------------------------------------------------------
3719 initialize and start new game
3720 =============================================================================
3725 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3726 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3727 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3729 boolean do_fading = (game_status == GAME_MODE_MAIN);
3732 int initial_move_dir = MV_DOWN;
3734 int initial_move_dir = MV_NONE;
3738 game_status = GAME_MODE_PLAYING;
3741 /* needed if different viewport properties defined for playing */
3742 ChangeViewportPropertiesIfNeeded();
3746 DrawCompleteVideoDisplay();
3750 InitGameControlValues();
3752 /* don't play tapes over network */
3753 network_playing = (options.network && !tape.playing);
3755 for (i = 0; i < MAX_PLAYERS; i++)
3757 struct PlayerInfo *player = &stored_player[i];
3759 player->index_nr = i;
3760 player->index_bit = (1 << i);
3761 player->element_nr = EL_PLAYER_1 + i;
3763 player->present = FALSE;
3764 player->active = FALSE;
3765 player->mapped = FALSE;
3767 player->killed = FALSE;
3768 player->reanimated = FALSE;
3771 player->effective_action = 0;
3772 player->programmed_action = 0;
3775 player->score_final = 0;
3777 player->gems_still_needed = level.gems_needed;
3778 player->sokobanfields_still_needed = 0;
3779 player->lights_still_needed = 0;
3780 player->friends_still_needed = 0;
3782 for (j = 0; j < MAX_NUM_KEYS; j++)
3783 player->key[j] = FALSE;
3785 player->num_white_keys = 0;
3787 player->dynabomb_count = 0;
3788 player->dynabomb_size = 1;
3789 player->dynabombs_left = 0;
3790 player->dynabomb_xl = FALSE;
3792 player->MovDir = initial_move_dir;
3795 player->GfxDir = initial_move_dir;
3796 player->GfxAction = ACTION_DEFAULT;
3798 player->StepFrame = 0;
3800 player->initial_element = player->element_nr;
3801 player->artwork_element =
3802 (level.use_artwork_element[i] ? level.artwork_element[i] :
3803 player->element_nr);
3804 player->use_murphy = FALSE;
3806 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3807 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3809 player->gravity = level.initial_player_gravity[i];
3811 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3813 player->actual_frame_counter = 0;
3815 player->step_counter = 0;
3817 player->last_move_dir = initial_move_dir;
3819 player->is_active = FALSE;
3821 player->is_waiting = FALSE;
3822 player->is_moving = FALSE;
3823 player->is_auto_moving = FALSE;
3824 player->is_digging = FALSE;
3825 player->is_snapping = FALSE;
3826 player->is_collecting = FALSE;
3827 player->is_pushing = FALSE;
3828 player->is_switching = FALSE;
3829 player->is_dropping = FALSE;
3830 player->is_dropping_pressed = FALSE;
3832 player->is_bored = FALSE;
3833 player->is_sleeping = FALSE;
3835 player->frame_counter_bored = -1;
3836 player->frame_counter_sleeping = -1;
3838 player->anim_delay_counter = 0;
3839 player->post_delay_counter = 0;
3841 player->dir_waiting = initial_move_dir;
3842 player->action_waiting = ACTION_DEFAULT;
3843 player->last_action_waiting = ACTION_DEFAULT;
3844 player->special_action_bored = ACTION_DEFAULT;
3845 player->special_action_sleeping = ACTION_DEFAULT;
3847 player->switch_x = -1;
3848 player->switch_y = -1;
3850 player->drop_x = -1;
3851 player->drop_y = -1;
3853 player->show_envelope = 0;
3855 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3857 player->push_delay = -1; /* initialized when pushing starts */
3858 player->push_delay_value = game.initial_push_delay_value;
3860 player->drop_delay = 0;
3861 player->drop_pressed_delay = 0;
3863 player->last_jx = -1;
3864 player->last_jy = -1;
3868 player->shield_normal_time_left = 0;
3869 player->shield_deadly_time_left = 0;
3871 player->inventory_infinite_element = EL_UNDEFINED;
3872 player->inventory_size = 0;
3874 if (level.use_initial_inventory[i])
3876 for (j = 0; j < level.initial_inventory_size[i]; j++)
3878 int element = level.initial_inventory_content[i][j];
3879 int collect_count = element_info[element].collect_count_initial;
3882 if (!IS_CUSTOM_ELEMENT(element))
3885 if (collect_count == 0)
3886 player->inventory_infinite_element = element;
3888 for (k = 0; k < collect_count; k++)
3889 if (player->inventory_size < MAX_INVENTORY_SIZE)
3890 player->inventory_element[player->inventory_size++] = element;
3894 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3895 SnapField(player, 0, 0);
3897 player->LevelSolved = FALSE;
3898 player->GameOver = FALSE;
3900 player->LevelSolved_GameWon = FALSE;
3901 player->LevelSolved_GameEnd = FALSE;
3902 player->LevelSolved_PanelOff = FALSE;
3903 player->LevelSolved_SaveTape = FALSE;
3904 player->LevelSolved_SaveScore = FALSE;
3905 player->LevelSolved_CountingTime = 0;
3906 player->LevelSolved_CountingScore = 0;
3908 map_player_action[i] = i;
3911 network_player_action_received = FALSE;
3913 #if defined(NETWORK_AVALIABLE)
3914 /* initial null action */
3915 if (network_playing)
3916 SendToServer_MovePlayer(MV_NONE);
3925 TimeLeft = level.time;
3928 ScreenMovDir = MV_NONE;
3932 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3934 AllPlayersGone = FALSE;
3936 game.no_time_limit = (level.time == 0);
3938 game.yamyam_content_nr = 0;
3939 game.robot_wheel_active = FALSE;
3940 game.magic_wall_active = FALSE;
3941 game.magic_wall_time_left = 0;
3942 game.light_time_left = 0;
3943 game.timegate_time_left = 0;
3944 game.switchgate_pos = 0;
3945 game.wind_direction = level.wind_direction_initial;
3947 #if !USE_PLAYER_GRAVITY
3948 game.gravity = FALSE;
3949 game.explosions_delayed = TRUE;
3952 game.lenses_time_left = 0;
3953 game.magnify_time_left = 0;
3955 game.ball_state = level.ball_state_initial;
3956 game.ball_content_nr = 0;
3958 game.envelope_active = FALSE;
3960 /* set focus to local player for network games, else to all players */
3961 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3962 game.centered_player_nr_next = game.centered_player_nr;
3963 game.set_centered_player = FALSE;
3965 if (network_playing && tape.recording)
3967 /* store client dependent player focus when recording network games */
3968 tape.centered_player_nr_next = game.centered_player_nr_next;
3969 tape.set_centered_player = TRUE;
3972 for (i = 0; i < NUM_BELTS; i++)
3974 game.belt_dir[i] = MV_NONE;
3975 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3978 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3979 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3981 SCAN_PLAYFIELD(x, y)
3983 Feld[x][y] = level.field[x][y];
3984 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3985 ChangeDelay[x][y] = 0;
3986 ChangePage[x][y] = -1;
3987 #if USE_NEW_CUSTOM_VALUE
3988 CustomValue[x][y] = 0; /* initialized in InitField() */
3990 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3992 WasJustMoving[x][y] = 0;
3993 WasJustFalling[x][y] = 0;
3994 CheckCollision[x][y] = 0;
3995 CheckImpact[x][y] = 0;
3997 Pushed[x][y] = FALSE;
3999 ChangeCount[x][y] = 0;
4000 ChangeEvent[x][y] = -1;
4002 ExplodePhase[x][y] = 0;
4003 ExplodeDelay[x][y] = 0;
4004 ExplodeField[x][y] = EX_TYPE_NONE;
4006 RunnerVisit[x][y] = 0;
4007 PlayerVisit[x][y] = 0;
4010 GfxRandom[x][y] = INIT_GFX_RANDOM();
4011 GfxElement[x][y] = EL_UNDEFINED;
4012 GfxAction[x][y] = ACTION_DEFAULT;
4013 GfxDir[x][y] = MV_NONE;
4014 GfxRedraw[x][y] = GFX_REDRAW_NONE;
4017 SCAN_PLAYFIELD(x, y)
4019 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
4021 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
4023 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
4026 InitField(x, y, TRUE);
4028 ResetGfxAnimation(x, y);
4033 for (i = 0; i < MAX_PLAYERS; i++)
4035 struct PlayerInfo *player = &stored_player[i];
4037 /* set number of special actions for bored and sleeping animation */
4038 player->num_special_action_bored =
4039 get_num_special_action(player->artwork_element,
4040 ACTION_BORING_1, ACTION_BORING_LAST);
4041 player->num_special_action_sleeping =
4042 get_num_special_action(player->artwork_element,
4043 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
4046 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
4047 emulate_sb ? EMU_SOKOBAN :
4048 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
4050 #if USE_NEW_ALL_SLIPPERY
4051 /* initialize type of slippery elements */
4052 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4054 if (!IS_CUSTOM_ELEMENT(i))
4056 /* default: elements slip down either to the left or right randomly */
4057 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
4059 /* SP style elements prefer to slip down on the left side */
4060 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
4061 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4063 /* BD style elements prefer to slip down on the left side */
4064 if (game.emulation == EMU_BOULDERDASH)
4065 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4070 /* initialize explosion and ignition delay */
4071 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4073 if (!IS_CUSTOM_ELEMENT(i))
4076 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4077 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4078 game.emulation == EMU_SUPAPLEX ? 3 : 2);
4079 int last_phase = (num_phase + 1) * delay;
4080 int half_phase = (num_phase / 2) * delay;
4082 element_info[i].explosion_delay = last_phase - 1;
4083 element_info[i].ignition_delay = half_phase;
4085 if (i == EL_BLACK_ORB)
4086 element_info[i].ignition_delay = 1;
4090 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
4091 element_info[i].explosion_delay = 1;
4093 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
4094 element_info[i].ignition_delay = 1;
4098 /* correct non-moving belts to start moving left */
4099 for (i = 0; i < NUM_BELTS; i++)
4100 if (game.belt_dir[i] == MV_NONE)
4101 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
4103 #if USE_NEW_PLAYER_ASSIGNMENTS
4104 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4105 /* choose default local player */
4106 local_player = &stored_player[0];
4108 for (i = 0; i < MAX_PLAYERS; i++)
4109 stored_player[i].connected = FALSE;
4111 local_player->connected = TRUE;
4112 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4116 /* try to guess locally connected team mode players (needed for correct
4117 assignment of player figures from level to locally playing players) */
4119 for (i = 0; i < MAX_PLAYERS; i++)
4120 if (tape.player_participates[i])
4121 stored_player[i].connected = TRUE;
4123 else if (setup.team_mode && !options.network)
4125 /* try to guess locally connected team mode players (needed for correct
4126 assignment of player figures from level to locally playing players) */
4128 for (i = 0; i < MAX_PLAYERS; i++)
4129 if (setup.input[i].use_joystick ||
4130 setup.input[i].key.left != KSYM_UNDEFINED)
4131 stored_player[i].connected = TRUE;
4135 for (i = 0; i < MAX_PLAYERS; i++)
4136 printf("::: player %d: %s\n", i,
4137 (stored_player[i].connected ? "connected" : "not connected"));
4139 for (i = 0; i < MAX_PLAYERS; i++)
4140 printf("::: player %d: %s\n", i,
4141 (stored_player[i].present ? "present" : "not present"));
4144 /* check if any connected player was not found in playfield */
4145 for (i = 0; i < MAX_PLAYERS; i++)
4147 struct PlayerInfo *player = &stored_player[i];
4149 if (player->connected && !player->present)
4151 struct PlayerInfo *field_player = NULL;
4154 printf("::: looking for field player for player %d ...\n", i);
4157 /* assign first free player found that is present in the playfield */
4159 /* first try: look for unmapped playfield player that is not connected */
4160 if (field_player == NULL)
4161 for (j = 0; j < MAX_PLAYERS; j++)
4162 if (stored_player[j].present &&
4163 !stored_player[j].mapped &&
4164 !stored_player[j].connected)
4165 field_player = &stored_player[j];
4167 /* second try: look for *any* unmapped playfield player */
4168 if (field_player == NULL)
4169 for (j = 0; j < MAX_PLAYERS; j++)
4170 if (stored_player[j].present &&
4171 !stored_player[j].mapped)
4172 field_player = &stored_player[j];
4174 if (field_player != NULL)
4176 int jx = field_player->jx, jy = field_player->jy;
4179 printf("::: found player figure %d\n", field_player->index_nr);
4182 player->present = FALSE;
4183 player->active = FALSE;
4185 field_player->present = TRUE;
4186 field_player->active = TRUE;
4189 player->initial_element = field_player->initial_element;
4190 player->artwork_element = field_player->artwork_element;
4192 player->block_last_field = field_player->block_last_field;
4193 player->block_delay_adjustment = field_player->block_delay_adjustment;
4196 StorePlayer[jx][jy] = field_player->element_nr;
4198 field_player->jx = field_player->last_jx = jx;
4199 field_player->jy = field_player->last_jy = jy;
4201 if (local_player == player)
4202 local_player = field_player;
4204 map_player_action[field_player->index_nr] = i;
4206 field_player->mapped = TRUE;
4209 printf("::: map_player_action[%d] == %d\n",
4210 field_player->index_nr, i);
4215 if (player->connected && player->present)
4216 player->mapped = TRUE;
4221 /* check if any connected player was not found in playfield */
4222 for (i = 0; i < MAX_PLAYERS; i++)
4224 struct PlayerInfo *player = &stored_player[i];
4226 if (player->connected && !player->present)
4228 for (j = 0; j < MAX_PLAYERS; j++)
4230 struct PlayerInfo *field_player = &stored_player[j];
4231 int jx = field_player->jx, jy = field_player->jy;
4233 /* assign first free player found that is present in the playfield */
4234 if (field_player->present && !field_player->connected)
4236 player->present = TRUE;
4237 player->active = TRUE;
4239 field_player->present = FALSE;
4240 field_player->active = FALSE;
4242 player->initial_element = field_player->initial_element;
4243 player->artwork_element = field_player->artwork_element;
4245 player->block_last_field = field_player->block_last_field;
4246 player->block_delay_adjustment = field_player->block_delay_adjustment;
4248 StorePlayer[jx][jy] = player->element_nr;
4250 player->jx = player->last_jx = jx;
4251 player->jy = player->last_jy = jy;
4261 printf("::: local_player->present == %d\n", local_player->present);
4266 /* when playing a tape, eliminate all players who do not participate */
4268 #if USE_NEW_PLAYER_ASSIGNMENTS
4269 for (i = 0; i < MAX_PLAYERS; i++)
4271 if (stored_player[i].active &&
4272 !tape.player_participates[map_player_action[i]])
4274 struct PlayerInfo *player = &stored_player[i];
4275 int jx = player->jx, jy = player->jy;
4277 player->active = FALSE;
4278 StorePlayer[jx][jy] = 0;
4279 Feld[jx][jy] = EL_EMPTY;
4283 for (i = 0; i < MAX_PLAYERS; i++)
4285 if (stored_player[i].active &&
4286 !tape.player_participates[i])
4288 struct PlayerInfo *player = &stored_player[i];
4289 int jx = player->jx, jy = player->jy;
4291 player->active = FALSE;
4292 StorePlayer[jx][jy] = 0;
4293 Feld[jx][jy] = EL_EMPTY;
4298 else if (!options.network && !setup.team_mode) /* && !tape.playing */
4300 /* when in single player mode, eliminate all but the first active player */
4302 for (i = 0; i < MAX_PLAYERS; i++)
4304 if (stored_player[i].active)
4306 for (j = i + 1; j < MAX_PLAYERS; j++)
4308 if (stored_player[j].active)
4310 struct PlayerInfo *player = &stored_player[j];
4311 int jx = player->jx, jy = player->jy;
4313 player->active = FALSE;
4314 player->present = FALSE;
4316 StorePlayer[jx][jy] = 0;
4317 Feld[jx][jy] = EL_EMPTY;
4324 /* when recording the game, store which players take part in the game */
4327 #if USE_NEW_PLAYER_ASSIGNMENTS
4328 for (i = 0; i < MAX_PLAYERS; i++)
4329 if (stored_player[i].connected)
4330 tape.player_participates[i] = TRUE;
4332 for (i = 0; i < MAX_PLAYERS; i++)
4333 if (stored_player[i].active)
4334 tape.player_participates[i] = TRUE;
4340 for (i = 0; i < MAX_PLAYERS; i++)
4342 struct PlayerInfo *player = &stored_player[i];
4344 printf("Player %d: present == %d, connected == %d, active == %d.\n",
4349 if (local_player == player)
4350 printf("Player %d is local player.\n", i+1);
4354 if (BorderElement == EL_EMPTY)
4357 SBX_Right = lev_fieldx - SCR_FIELDX;
4359 SBY_Lower = lev_fieldy - SCR_FIELDY;
4364 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4366 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4371 if (lev_fieldx + (SBX_Left < 0 ? 2 : 0) <= SCR_FIELDX)
4372 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4374 if (lev_fieldy + (SBY_Upper < 0 ? 2 : 0) <= SCR_FIELDY)
4375 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4377 if (EVEN(SCR_FIELDX))
4379 if (EVEN(SCR_FIELDY))
4384 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4385 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4387 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4388 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4391 /* if local player not found, look for custom element that might create
4392 the player (make some assumptions about the right custom element) */
4393 if (!local_player->present)
4395 int start_x = 0, start_y = 0;
4396 int found_rating = 0;
4397 int found_element = EL_UNDEFINED;
4398 int player_nr = local_player->index_nr;
4400 SCAN_PLAYFIELD(x, y)
4402 int element = Feld[x][y];
4407 if (level.use_start_element[player_nr] &&
4408 level.start_element[player_nr] == element &&
4415 found_element = element;
4418 if (!IS_CUSTOM_ELEMENT(element))
4421 if (CAN_CHANGE(element))
4423 for (i = 0; i < element_info[element].num_change_pages; i++)
4425 /* check for player created from custom element as single target */
4426 content = element_info[element].change_page[i].target_element;
4427 is_player = ELEM_IS_PLAYER(content);
4429 if (is_player && (found_rating < 3 ||
4430 (found_rating == 3 && element < found_element)))
4436 found_element = element;
4441 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4443 /* check for player created from custom element as explosion content */
4444 content = element_info[element].content.e[xx][yy];
4445 is_player = ELEM_IS_PLAYER(content);
4447 if (is_player && (found_rating < 2 ||
4448 (found_rating == 2 && element < found_element)))
4450 start_x = x + xx - 1;
4451 start_y = y + yy - 1;
4454 found_element = element;
4457 if (!CAN_CHANGE(element))
4460 for (i = 0; i < element_info[element].num_change_pages; i++)
4462 /* check for player created from custom element as extended target */
4464 element_info[element].change_page[i].target_content.e[xx][yy];
4466 is_player = ELEM_IS_PLAYER(content);
4468 if (is_player && (found_rating < 1 ||
4469 (found_rating == 1 && element < found_element)))
4471 start_x = x + xx - 1;
4472 start_y = y + yy - 1;
4475 found_element = element;
4481 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
4482 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4485 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4486 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4491 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
4492 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4493 local_player->jx - MIDPOSX);
4495 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4496 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4497 local_player->jy - MIDPOSY);
4501 printf("::: %d, %d (initial)\n", scroll_x, scroll_y);
4505 /* do not use PLAYING mask for fading out from main screen */
4506 game_status = GAME_MODE_MAIN;
4511 if (!game.restart_level)
4512 CloseDoor(DOOR_CLOSE_1);
4515 if (level_editor_test_game)
4516 FadeSkipNextFadeIn();
4518 FadeSetEnterScreen();
4520 if (level_editor_test_game)
4521 fading = fading_none;
4523 fading = menu.destination;
4527 FadeOut(REDRAW_FIELD);
4530 FadeOut(REDRAW_FIELD);
4534 game_status = GAME_MODE_PLAYING;
4537 /* !!! FIX THIS (START) !!! */
4538 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4540 InitGameEngine_EM();
4542 /* blit playfield from scroll buffer to normal back buffer for fading in */
4543 BlitScreenToBitmap_EM(backbuffer);
4545 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4547 InitGameEngine_SP();
4549 /* blit playfield from scroll buffer to normal back buffer for fading in */
4550 BlitScreenToBitmap_SP(backbuffer);
4557 /* after drawing the level, correct some elements */
4558 if (game.timegate_time_left == 0)
4559 CloseAllOpenTimegates();
4562 BlitScreenToBitmap(backbuffer);
4564 /* blit playfield from scroll buffer to normal back buffer for fading in */
4565 if (setup.soft_scrolling)
4566 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4569 redraw_mask |= REDRAW_FROM_BACKBUFFER;
4571 /* !!! FIX THIS (END) !!! */
4574 FadeIn(REDRAW_FIELD);
4577 FadeIn(REDRAW_FIELD);
4582 if (!game.restart_level)
4584 /* copy default game door content to main double buffer */
4587 /* !!! CHECK AGAIN !!! */
4588 SetPanelBackground();
4589 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4590 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4592 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
4594 /* (ClearRectangle() only needed if panel bitmap is smaller than panel) */
4595 ClearRectangle(drawto, DX, DY, DXSIZE, DYSIZE);
4596 BlitBitmap(gfx->bitmap, drawto, gfx->src_x, gfx->src_y,
4597 MIN(gfx->width, DXSIZE), MIN(gfx->height, DYSIZE), DX, DY);
4600 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4601 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4605 SetPanelBackground();
4606 SetDrawBackgroundMask(REDRAW_DOOR_1);
4609 UpdateAndDisplayGameControlValues();
4611 UpdateGameDoorValues();
4612 DrawGameDoorValues();
4615 if (!game.restart_level)
4619 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4620 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4621 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4625 /* copy actual game door content to door double buffer for OpenDoor() */
4626 BlitBitmap(drawto, bitmap_db_door,
4627 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4629 OpenDoor(DOOR_OPEN_ALL);
4631 PlaySound(SND_GAME_STARTING);
4633 if (setup.sound_music)
4636 KeyboardAutoRepeatOffUnlessAutoplay();
4640 for (i = 0; i < MAX_PLAYERS; i++)
4641 printf("Player %d %sactive.\n",
4642 i + 1, (stored_player[i].active ? "" : "not "));
4653 if (!game.restart_level && !tape.playing)
4655 LevelStats_incPlayed(level_nr);
4657 SaveLevelSetup_SeriesInfo();
4660 printf("::: PLAYING LEVEL (%d)\n", LevelStats_getPlayed(level_nr));
4664 game.restart_level = FALSE;
4667 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4669 /* this is used for non-R'n'D game engines to update certain engine values */
4671 /* needed to determine if sounds are played within the visible screen area */
4672 scroll_x = actual_scroll_x;
4673 scroll_y = actual_scroll_y;
4676 void InitMovDir(int x, int y)
4678 int i, element = Feld[x][y];
4679 static int xy[4][2] =
4686 static int direction[3][4] =
4688 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4689 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4690 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4699 Feld[x][y] = EL_BUG;
4700 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4703 case EL_SPACESHIP_RIGHT:
4704 case EL_SPACESHIP_UP:
4705 case EL_SPACESHIP_LEFT:
4706 case EL_SPACESHIP_DOWN:
4707 Feld[x][y] = EL_SPACESHIP;
4708 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4711 case EL_BD_BUTTERFLY_RIGHT:
4712 case EL_BD_BUTTERFLY_UP:
4713 case EL_BD_BUTTERFLY_LEFT:
4714 case EL_BD_BUTTERFLY_DOWN:
4715 Feld[x][y] = EL_BD_BUTTERFLY;
4716 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4719 case EL_BD_FIREFLY_RIGHT:
4720 case EL_BD_FIREFLY_UP:
4721 case EL_BD_FIREFLY_LEFT:
4722 case EL_BD_FIREFLY_DOWN:
4723 Feld[x][y] = EL_BD_FIREFLY;
4724 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4727 case EL_PACMAN_RIGHT:
4729 case EL_PACMAN_LEFT:
4730 case EL_PACMAN_DOWN:
4731 Feld[x][y] = EL_PACMAN;
4732 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4735 case EL_YAMYAM_LEFT:
4736 case EL_YAMYAM_RIGHT:
4738 case EL_YAMYAM_DOWN:
4739 Feld[x][y] = EL_YAMYAM;
4740 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4743 case EL_SP_SNIKSNAK:
4744 MovDir[x][y] = MV_UP;
4747 case EL_SP_ELECTRON:
4748 MovDir[x][y] = MV_LEFT;
4755 Feld[x][y] = EL_MOLE;
4756 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4760 if (IS_CUSTOM_ELEMENT(element))
4762 struct ElementInfo *ei = &element_info[element];
4763 int move_direction_initial = ei->move_direction_initial;
4764 int move_pattern = ei->move_pattern;
4766 if (move_direction_initial == MV_START_PREVIOUS)
4768 if (MovDir[x][y] != MV_NONE)
4771 move_direction_initial = MV_START_AUTOMATIC;
4774 if (move_direction_initial == MV_START_RANDOM)
4775 MovDir[x][y] = 1 << RND(4);
4776 else if (move_direction_initial & MV_ANY_DIRECTION)
4777 MovDir[x][y] = move_direction_initial;
4778 else if (move_pattern == MV_ALL_DIRECTIONS ||
4779 move_pattern == MV_TURNING_LEFT ||
4780 move_pattern == MV_TURNING_RIGHT ||
4781 move_pattern == MV_TURNING_LEFT_RIGHT ||
4782 move_pattern == MV_TURNING_RIGHT_LEFT ||
4783 move_pattern == MV_TURNING_RANDOM)
4784 MovDir[x][y] = 1 << RND(4);
4785 else if (move_pattern == MV_HORIZONTAL)
4786 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4787 else if (move_pattern == MV_VERTICAL)
4788 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4789 else if (move_pattern & MV_ANY_DIRECTION)
4790 MovDir[x][y] = element_info[element].move_pattern;
4791 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4792 move_pattern == MV_ALONG_RIGHT_SIDE)
4794 /* use random direction as default start direction */
4795 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4796 MovDir[x][y] = 1 << RND(4);
4798 for (i = 0; i < NUM_DIRECTIONS; i++)
4800 int x1 = x + xy[i][0];
4801 int y1 = y + xy[i][1];
4803 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4805 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4806 MovDir[x][y] = direction[0][i];
4808 MovDir[x][y] = direction[1][i];
4817 MovDir[x][y] = 1 << RND(4);
4819 if (element != EL_BUG &&
4820 element != EL_SPACESHIP &&
4821 element != EL_BD_BUTTERFLY &&
4822 element != EL_BD_FIREFLY)
4825 for (i = 0; i < NUM_DIRECTIONS; i++)
4827 int x1 = x + xy[i][0];
4828 int y1 = y + xy[i][1];
4830 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4832 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4834 MovDir[x][y] = direction[0][i];
4837 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4838 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4840 MovDir[x][y] = direction[1][i];
4849 GfxDir[x][y] = MovDir[x][y];
4852 void InitAmoebaNr(int x, int y)
4855 int group_nr = AmoebeNachbarNr(x, y);
4859 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4861 if (AmoebaCnt[i] == 0)
4869 AmoebaNr[x][y] = group_nr;
4870 AmoebaCnt[group_nr]++;
4871 AmoebaCnt2[group_nr]++;
4874 static void PlayerWins(struct PlayerInfo *player)
4876 player->LevelSolved = TRUE;
4877 player->GameOver = TRUE;
4879 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4880 level.native_em_level->lev->score : player->score);
4882 player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4884 player->LevelSolved_CountingScore = player->score_final;
4889 static int time, time_final;
4890 static int score, score_final;
4891 static int game_over_delay_1 = 0;
4892 static int game_over_delay_2 = 0;
4893 int game_over_delay_value_1 = 50;
4894 int game_over_delay_value_2 = 50;
4896 if (!local_player->LevelSolved_GameWon)
4900 /* do not start end game actions before the player stops moving (to exit) */
4901 if (local_player->MovPos)
4904 local_player->LevelSolved_GameWon = TRUE;
4905 local_player->LevelSolved_SaveTape = tape.recording;
4906 local_player->LevelSolved_SaveScore = !tape.playing;
4910 LevelStats_incSolved(level_nr);
4912 SaveLevelSetup_SeriesInfo();
4915 printf("::: LEVEL SOLVED (%d)\n", LevelStats_getSolved(level_nr));
4919 if (tape.auto_play) /* tape might already be stopped here */
4920 tape.auto_play_level_solved = TRUE;
4926 game_over_delay_1 = game_over_delay_value_1;
4927 game_over_delay_2 = game_over_delay_value_2;
4929 time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4930 score = score_final = local_player->score_final;
4935 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4937 else if (game.no_time_limit && TimePlayed < 999)
4940 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4943 local_player->score_final = score_final;
4945 if (level_editor_test_game)
4948 score = score_final;
4951 local_player->LevelSolved_CountingTime = time;
4952 local_player->LevelSolved_CountingScore = score;
4954 game_panel_controls[GAME_PANEL_TIME].value = time;
4955 game_panel_controls[GAME_PANEL_SCORE].value = score;
4957 DisplayGameControlValues();
4959 DrawGameValue_Time(time);
4960 DrawGameValue_Score(score);
4964 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4966 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4968 /* close exit door after last player */
4969 if ((AllPlayersGone &&
4970 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4971 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4972 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4973 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4974 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4976 int element = Feld[ExitX][ExitY];
4979 if (element == EL_EM_EXIT_OPEN ||
4980 element == EL_EM_STEEL_EXIT_OPEN)
4987 Feld[ExitX][ExitY] =
4988 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4989 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4990 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4991 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4992 EL_EM_STEEL_EXIT_CLOSING);
4994 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4998 /* player disappears */
4999 DrawLevelField(ExitX, ExitY);
5002 for (i = 0; i < MAX_PLAYERS; i++)
5004 struct PlayerInfo *player = &stored_player[i];
5006 if (player->present)
5008 RemovePlayer(player);
5010 /* player disappears */
5011 DrawLevelField(player->jx, player->jy);
5016 PlaySound(SND_GAME_WINNING);
5019 if (game_over_delay_1 > 0)
5021 game_over_delay_1--;
5026 if (time != time_final)
5028 int time_to_go = ABS(time_final - time);
5029 int time_count_dir = (time < time_final ? +1 : -1);
5030 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
5032 time += time_count_steps * time_count_dir;
5033 score += time_count_steps * level.score[SC_TIME_BONUS];
5036 local_player->LevelSolved_CountingTime = time;
5037 local_player->LevelSolved_CountingScore = score;
5039 game_panel_controls[GAME_PANEL_TIME].value = time;
5040 game_panel_controls[GAME_PANEL_SCORE].value = score;
5042 DisplayGameControlValues();
5044 DrawGameValue_Time(time);
5045 DrawGameValue_Score(score);
5048 if (time == time_final)
5049 StopSound(SND_GAME_LEVELTIME_BONUS);
5050 else if (setup.sound_loops)
5051 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5053 PlaySound(SND_GAME_LEVELTIME_BONUS);
5058 local_player->LevelSolved_PanelOff = TRUE;
5060 if (game_over_delay_2 > 0)
5062 game_over_delay_2--;
5075 boolean raise_level = FALSE;
5077 local_player->LevelSolved_GameEnd = TRUE;
5079 CloseDoor(DOOR_CLOSE_1);
5081 if (local_player->LevelSolved_SaveTape)
5088 SaveTapeChecked(tape.level_nr); /* ask to save tape */
5090 SaveTape(tape.level_nr); /* ask to save tape */
5094 if (level_editor_test_game)
5096 game_status = GAME_MODE_MAIN;
5099 DrawAndFadeInMainMenu(REDRAW_FIELD);
5107 if (!local_player->LevelSolved_SaveScore)
5110 FadeOut(REDRAW_FIELD);
5113 game_status = GAME_MODE_MAIN;
5115 DrawAndFadeInMainMenu(REDRAW_FIELD);
5120 if (level_nr == leveldir_current->handicap_level)
5122 leveldir_current->handicap_level++;
5124 SaveLevelSetup_SeriesInfo();
5127 if (level_nr < leveldir_current->last_level)
5128 raise_level = TRUE; /* advance to next level */
5130 if ((hi_pos = NewHiScore()) >= 0)
5132 game_status = GAME_MODE_SCORES;
5134 DrawHallOfFame(hi_pos);
5145 FadeOut(REDRAW_FIELD);
5148 game_status = GAME_MODE_MAIN;
5156 DrawAndFadeInMainMenu(REDRAW_FIELD);
5165 LoadScore(level_nr);
5167 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5168 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
5171 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
5173 if (local_player->score_final > highscore[k].Score)
5175 /* player has made it to the hall of fame */
5177 if (k < MAX_SCORE_ENTRIES - 1)
5179 int m = MAX_SCORE_ENTRIES - 1;
5182 for (l = k; l < MAX_SCORE_ENTRIES; l++)
5183 if (strEqual(setup.player_name, highscore[l].Name))
5185 if (m == k) /* player's new highscore overwrites his old one */
5189 for (l = m; l > k; l--)
5191 strcpy(highscore[l].Name, highscore[l - 1].Name);
5192 highscore[l].Score = highscore[l - 1].Score;
5199 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5200 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5201 highscore[k].Score = local_player->score_final;
5207 else if (!strncmp(setup.player_name, highscore[k].Name,
5208 MAX_PLAYER_NAME_LEN))
5209 break; /* player already there with a higher score */
5215 SaveScore(level_nr);
5220 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
5222 int element = Feld[x][y];
5223 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5224 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5225 int horiz_move = (dx != 0);
5226 int sign = (horiz_move ? dx : dy);
5227 int step = sign * element_info[element].move_stepsize;
5229 /* special values for move stepsize for spring and things on conveyor belt */
5232 if (CAN_FALL(element) &&
5233 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5234 step = sign * MOVE_STEPSIZE_NORMAL / 2;
5235 else if (element == EL_SPRING)
5236 step = sign * MOVE_STEPSIZE_NORMAL * 2;
5242 inline static int getElementMoveStepsize(int x, int y)
5244 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5247 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5249 if (player->GfxAction != action || player->GfxDir != dir)
5252 printf("Player frame reset! (%d => %d, %d => %d)\n",
5253 player->GfxAction, action, player->GfxDir, dir);
5256 player->GfxAction = action;
5257 player->GfxDir = dir;
5259 player->StepFrame = 0;
5263 #if USE_GFX_RESET_GFX_ANIMATION
5264 static void ResetGfxFrame(int x, int y, boolean redraw)
5266 int element = Feld[x][y];
5267 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5268 int last_gfx_frame = GfxFrame[x][y];
5270 if (graphic_info[graphic].anim_global_sync)
5271 GfxFrame[x][y] = FrameCounter;
5272 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5273 GfxFrame[x][y] = CustomValue[x][y];
5274 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5275 GfxFrame[x][y] = element_info[element].collect_score;
5276 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5277 GfxFrame[x][y] = ChangeDelay[x][y];
5279 if (redraw && GfxFrame[x][y] != last_gfx_frame)
5280 DrawLevelGraphicAnimation(x, y, graphic);
5284 static void ResetGfxAnimation(int x, int y)
5286 GfxAction[x][y] = ACTION_DEFAULT;
5287 GfxDir[x][y] = MovDir[x][y];
5290 #if USE_GFX_RESET_GFX_ANIMATION
5291 ResetGfxFrame(x, y, FALSE);
5295 static void ResetRandomAnimationValue(int x, int y)
5297 GfxRandom[x][y] = INIT_GFX_RANDOM();
5300 void InitMovingField(int x, int y, int direction)
5302 int element = Feld[x][y];
5303 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5304 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5307 boolean is_moving_before, is_moving_after;
5309 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5312 /* check if element was/is moving or being moved before/after mode change */
5315 is_moving_before = (WasJustMoving[x][y] != 0);
5317 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5318 is_moving_before = WasJustMoving[x][y];
5321 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5323 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
5325 /* reset animation only for moving elements which change direction of moving
5326 or which just started or stopped moving
5327 (else CEs with property "can move" / "not moving" are reset each frame) */
5328 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5330 if (is_moving_before != is_moving_after ||
5331 direction != MovDir[x][y])
5332 ResetGfxAnimation(x, y);
5334 if ((is_moving_before || is_moving_after) && !continues_moving)
5335 ResetGfxAnimation(x, y);
5338 if (!continues_moving)
5339 ResetGfxAnimation(x, y);
5342 MovDir[x][y] = direction;
5343 GfxDir[x][y] = direction;
5345 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5346 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5347 direction == MV_DOWN && CAN_FALL(element) ?
5348 ACTION_FALLING : ACTION_MOVING);
5350 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5351 ACTION_FALLING : ACTION_MOVING);
5354 /* this is needed for CEs with property "can move" / "not moving" */
5356 if (is_moving_after)
5358 if (Feld[newx][newy] == EL_EMPTY)
5359 Feld[newx][newy] = EL_BLOCKED;
5361 MovDir[newx][newy] = MovDir[x][y];
5363 #if USE_NEW_CUSTOM_VALUE
5364 CustomValue[newx][newy] = CustomValue[x][y];
5367 GfxFrame[newx][newy] = GfxFrame[x][y];
5368 GfxRandom[newx][newy] = GfxRandom[x][y];
5369 GfxAction[newx][newy] = GfxAction[x][y];
5370 GfxDir[newx][newy] = GfxDir[x][y];
5374 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5376 int direction = MovDir[x][y];
5377 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5378 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
5384 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5386 int oldx = x, oldy = y;
5387 int direction = MovDir[x][y];
5389 if (direction == MV_LEFT)
5391 else if (direction == MV_RIGHT)
5393 else if (direction == MV_UP)
5395 else if (direction == MV_DOWN)
5398 *comes_from_x = oldx;
5399 *comes_from_y = oldy;
5402 int MovingOrBlocked2Element(int x, int y)
5404 int element = Feld[x][y];
5406 if (element == EL_BLOCKED)
5410 Blocked2Moving(x, y, &oldx, &oldy);
5411 return Feld[oldx][oldy];
5417 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5419 /* like MovingOrBlocked2Element(), but if element is moving
5420 and (x,y) is the field the moving element is just leaving,
5421 return EL_BLOCKED instead of the element value */
5422 int element = Feld[x][y];
5424 if (IS_MOVING(x, y))
5426 if (element == EL_BLOCKED)
5430 Blocked2Moving(x, y, &oldx, &oldy);
5431 return Feld[oldx][oldy];
5440 static void RemoveField(int x, int y)
5442 Feld[x][y] = EL_EMPTY;
5448 #if USE_NEW_CUSTOM_VALUE
5449 CustomValue[x][y] = 0;
5453 ChangeDelay[x][y] = 0;
5454 ChangePage[x][y] = -1;
5455 Pushed[x][y] = FALSE;
5458 ExplodeField[x][y] = EX_TYPE_NONE;
5461 GfxElement[x][y] = EL_UNDEFINED;
5462 GfxAction[x][y] = ACTION_DEFAULT;
5463 GfxDir[x][y] = MV_NONE;
5465 /* !!! this would prevent the removed tile from being redrawn !!! */
5466 GfxRedraw[x][y] = GFX_REDRAW_NONE;
5470 void RemoveMovingField(int x, int y)
5472 int oldx = x, oldy = y, newx = x, newy = y;
5473 int element = Feld[x][y];
5474 int next_element = EL_UNDEFINED;
5476 if (element != EL_BLOCKED && !IS_MOVING(x, y))
5479 if (IS_MOVING(x, y))
5481 Moving2Blocked(x, y, &newx, &newy);
5483 if (Feld[newx][newy] != EL_BLOCKED)
5485 /* element is moving, but target field is not free (blocked), but
5486 already occupied by something different (example: acid pool);
5487 in this case, only remove the moving field, but not the target */
5489 RemoveField(oldx, oldy);
5491 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5493 TEST_DrawLevelField(oldx, oldy);
5498 else if (element == EL_BLOCKED)
5500 Blocked2Moving(x, y, &oldx, &oldy);
5501 if (!IS_MOVING(oldx, oldy))
5505 if (element == EL_BLOCKED &&
5506 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5507 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5508 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5509 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5510 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5511 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5512 next_element = get_next_element(Feld[oldx][oldy]);
5514 RemoveField(oldx, oldy);
5515 RemoveField(newx, newy);
5517 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5519 if (next_element != EL_UNDEFINED)
5520 Feld[oldx][oldy] = next_element;
5522 TEST_DrawLevelField(oldx, oldy);
5523 TEST_DrawLevelField(newx, newy);
5526 void DrawDynamite(int x, int y)
5528 int sx = SCREENX(x), sy = SCREENY(y);
5529 int graphic = el2img(Feld[x][y]);
5532 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5535 if (IS_WALKABLE_INSIDE(Back[x][y]))
5539 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5540 else if (Store[x][y])
5541 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5543 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5545 if (Back[x][y] || Store[x][y])
5546 DrawGraphicThruMask(sx, sy, graphic, frame);
5548 DrawGraphic(sx, sy, graphic, frame);
5551 void CheckDynamite(int x, int y)
5553 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5557 if (MovDelay[x][y] != 0)
5560 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5566 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5571 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5573 boolean num_checked_players = 0;
5576 for (i = 0; i < MAX_PLAYERS; i++)
5578 if (stored_player[i].active)
5580 int sx = stored_player[i].jx;
5581 int sy = stored_player[i].jy;
5583 if (num_checked_players == 0)
5590 *sx1 = MIN(*sx1, sx);
5591 *sy1 = MIN(*sy1, sy);
5592 *sx2 = MAX(*sx2, sx);
5593 *sy2 = MAX(*sy2, sy);
5596 num_checked_players++;
5601 static boolean checkIfAllPlayersFitToScreen_RND()
5603 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5605 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5607 return (sx2 - sx1 < SCR_FIELDX &&
5608 sy2 - sy1 < SCR_FIELDY);
5611 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5613 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5615 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5617 *sx = (sx1 + sx2) / 2;
5618 *sy = (sy1 + sy2) / 2;
5621 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5622 boolean center_screen, boolean quick_relocation)
5624 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5625 boolean no_delay = (tape.warp_forward);
5626 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5627 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5629 if (quick_relocation)
5631 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5633 if (!level.shifted_relocation || center_screen)
5635 /* quick relocation (without scrolling), with centering of screen */
5637 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5638 x > SBX_Right + MIDPOSX ? SBX_Right :
5641 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5642 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5647 /* quick relocation (without scrolling), but do not center screen */
5649 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5650 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5653 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5654 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5657 int offset_x = x + (scroll_x - center_scroll_x);
5658 int offset_y = y + (scroll_y - center_scroll_y);
5660 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5661 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5662 offset_x - MIDPOSX);
5664 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5665 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5666 offset_y - MIDPOSY);
5672 if (!level.shifted_relocation || center_screen)
5674 /* quick relocation (without scrolling), with centering of screen */
5676 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5677 x > SBX_Right + MIDPOSX ? SBX_Right :
5680 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5681 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5686 /* quick relocation (without scrolling), but do not center screen */
5688 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5689 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5692 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5693 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5696 int offset_x = x + (scroll_x - center_scroll_x);
5697 int offset_y = y + (scroll_y - center_scroll_y);
5699 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5700 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5701 offset_x - MIDPOSX);
5703 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5704 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5705 offset_y - MIDPOSY);
5708 /* quick relocation (without scrolling), inside visible screen area */
5710 int offset = game.scroll_delay_value;
5712 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
5713 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5714 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5716 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
5717 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5718 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5720 /* don't scroll over playfield boundaries */
5721 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5722 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5724 /* don't scroll over playfield boundaries */
5725 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5726 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5730 RedrawPlayfield(TRUE, 0,0,0,0);
5735 int scroll_xx, scroll_yy;
5737 if (!level.shifted_relocation || center_screen)
5739 /* visible relocation (with scrolling), with centering of screen */
5741 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5742 x > SBX_Right + MIDPOSX ? SBX_Right :
5745 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5746 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5751 /* visible relocation (with scrolling), but do not center screen */
5753 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5754 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5757 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5758 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5761 int offset_x = x + (scroll_x - center_scroll_x);
5762 int offset_y = y + (scroll_y - center_scroll_y);
5764 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5765 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5766 offset_x - MIDPOSX);
5768 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5769 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5770 offset_y - MIDPOSY);
5775 /* visible relocation (with scrolling), with centering of screen */
5777 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5778 x > SBX_Right + MIDPOSX ? SBX_Right :
5781 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5782 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5786 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5788 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5791 int fx = FX, fy = FY;
5793 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5794 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5796 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5802 fx += dx * TILEX / 2;
5803 fy += dy * TILEY / 2;
5805 ScrollLevel(dx, dy);
5808 /* scroll in two steps of half tile size to make things smoother */
5809 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5811 Delay(wait_delay_value);
5813 /* scroll second step to align at full tile size */
5815 Delay(wait_delay_value);
5820 Delay(wait_delay_value);
5824 void RelocatePlayer(int jx, int jy, int el_player_raw)
5826 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5827 int player_nr = GET_PLAYER_NR(el_player);
5828 struct PlayerInfo *player = &stored_player[player_nr];
5829 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5830 boolean no_delay = (tape.warp_forward);
5831 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5832 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5833 int old_jx = player->jx;
5834 int old_jy = player->jy;
5835 int old_element = Feld[old_jx][old_jy];
5836 int element = Feld[jx][jy];
5837 boolean player_relocated = (old_jx != jx || old_jy != jy);
5839 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5840 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5841 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5842 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5843 int leave_side_horiz = move_dir_horiz;
5844 int leave_side_vert = move_dir_vert;
5845 int enter_side = enter_side_horiz | enter_side_vert;
5846 int leave_side = leave_side_horiz | leave_side_vert;
5848 if (player->GameOver) /* do not reanimate dead player */
5851 if (!player_relocated) /* no need to relocate the player */
5854 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5856 RemoveField(jx, jy); /* temporarily remove newly placed player */
5857 DrawLevelField(jx, jy);
5860 if (player->present)
5862 while (player->MovPos)
5864 ScrollPlayer(player, SCROLL_GO_ON);
5865 ScrollScreen(NULL, SCROLL_GO_ON);
5867 AdvanceFrameAndPlayerCounters(player->index_nr);
5872 Delay(wait_delay_value);
5875 DrawPlayer(player); /* needed here only to cleanup last field */
5876 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5878 player->is_moving = FALSE;
5881 if (IS_CUSTOM_ELEMENT(old_element))
5882 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5884 player->index_bit, leave_side);
5886 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5888 player->index_bit, leave_side);
5890 Feld[jx][jy] = el_player;
5891 InitPlayerField(jx, jy, el_player, TRUE);
5893 /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5894 possible that the relocation target field did not contain a player element,
5895 but a walkable element, to which the new player was relocated -- in this
5896 case, restore that (already initialized!) element on the player field */
5897 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5899 Feld[jx][jy] = element; /* restore previously existing element */
5901 /* !!! do not initialize already initialized element a second time !!! */
5902 /* (this causes at least problems with "element creation" CE trigger for
5903 already existing elements, and existing Sokoban fields counted twice) */
5904 InitField(jx, jy, FALSE);
5908 /* only visually relocate centered player */
5909 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5910 FALSE, level.instant_relocation);
5912 TestIfPlayerTouchesBadThing(jx, jy);
5913 TestIfPlayerTouchesCustomElement(jx, jy);
5915 if (IS_CUSTOM_ELEMENT(element))
5916 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5917 player->index_bit, enter_side);
5919 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5920 player->index_bit, enter_side);
5923 if (player->is_switching)
5925 /* ensure that relocation while still switching an element does not cause
5926 a new element to be treated as also switched directly after relocation
5927 (this is important for teleporter switches that teleport the player to
5928 a place where another teleporter switch is in the same direction, which
5929 would then incorrectly be treated as immediately switched before the
5930 direction key that caused the switch was released) */
5932 player->switch_x += jx - old_jx;
5933 player->switch_y += jy - old_jy;
5938 void Explode(int ex, int ey, int phase, int mode)
5944 /* !!! eliminate this variable !!! */
5945 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5947 if (game.explosions_delayed)
5949 ExplodeField[ex][ey] = mode;
5953 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5955 int center_element = Feld[ex][ey];
5956 int artwork_element, explosion_element; /* set these values later */
5959 /* --- This is only really needed (and now handled) in "Impact()". --- */
5960 /* do not explode moving elements that left the explode field in time */
5961 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5962 center_element == EL_EMPTY &&
5963 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5968 /* !!! at this place, the center element may be EL_BLOCKED !!! */
5969 if (mode == EX_TYPE_NORMAL ||
5970 mode == EX_TYPE_CENTER ||
5971 mode == EX_TYPE_CROSS)
5972 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5975 /* remove things displayed in background while burning dynamite */
5976 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5979 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5981 /* put moving element to center field (and let it explode there) */
5982 center_element = MovingOrBlocked2Element(ex, ey);
5983 RemoveMovingField(ex, ey);
5984 Feld[ex][ey] = center_element;
5987 /* now "center_element" is finally determined -- set related values now */
5988 artwork_element = center_element; /* for custom player artwork */
5989 explosion_element = center_element; /* for custom player artwork */
5991 if (IS_PLAYER(ex, ey))
5993 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5995 artwork_element = stored_player[player_nr].artwork_element;
5997 if (level.use_explosion_element[player_nr])
5999 explosion_element = level.explosion_element[player_nr];
6000 artwork_element = explosion_element;
6005 if (mode == EX_TYPE_NORMAL ||
6006 mode == EX_TYPE_CENTER ||
6007 mode == EX_TYPE_CROSS)
6008 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
6011 last_phase = element_info[explosion_element].explosion_delay + 1;
6013 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
6015 int xx = x - ex + 1;
6016 int yy = y - ey + 1;
6019 if (!IN_LEV_FIELD(x, y) ||
6020 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
6021 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
6024 element = Feld[x][y];
6026 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
6028 element = MovingOrBlocked2Element(x, y);
6030 if (!IS_EXPLOSION_PROOF(element))
6031 RemoveMovingField(x, y);
6034 /* indestructible elements can only explode in center (but not flames) */
6035 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
6036 mode == EX_TYPE_BORDER)) ||
6037 element == EL_FLAMES)
6040 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
6041 behaviour, for example when touching a yamyam that explodes to rocks
6042 with active deadly shield, a rock is created under the player !!! */
6043 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
6045 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
6046 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
6047 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
6049 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
6052 if (IS_ACTIVE_BOMB(element))
6054 /* re-activate things under the bomb like gate or penguin */
6055 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
6062 /* save walkable background elements while explosion on same tile */
6063 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
6064 (x != ex || y != ey || mode == EX_TYPE_BORDER))
6065 Back[x][y] = element;
6067 /* ignite explodable elements reached by other explosion */
6068 if (element == EL_EXPLOSION)
6069 element = Store2[x][y];
6071 if (AmoebaNr[x][y] &&
6072 (element == EL_AMOEBA_FULL ||
6073 element == EL_BD_AMOEBA ||
6074 element == EL_AMOEBA_GROWING))
6076 AmoebaCnt[AmoebaNr[x][y]]--;
6077 AmoebaCnt2[AmoebaNr[x][y]]--;
6082 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
6084 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
6086 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
6088 if (PLAYERINFO(ex, ey)->use_murphy)
6089 Store[x][y] = EL_EMPTY;
6092 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
6093 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
6094 else if (ELEM_IS_PLAYER(center_element))
6095 Store[x][y] = EL_EMPTY;
6096 else if (center_element == EL_YAMYAM)
6097 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
6098 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
6099 Store[x][y] = element_info[center_element].content.e[xx][yy];
6101 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6102 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6103 otherwise) -- FIX THIS !!! */
6104 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6105 Store[x][y] = element_info[element].content.e[1][1];
6107 else if (!CAN_EXPLODE(element))
6108 Store[x][y] = element_info[element].content.e[1][1];
6111 Store[x][y] = EL_EMPTY;
6113 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6114 center_element == EL_AMOEBA_TO_DIAMOND)
6115 Store2[x][y] = element;
6117 Feld[x][y] = EL_EXPLOSION;
6118 GfxElement[x][y] = artwork_element;
6120 ExplodePhase[x][y] = 1;
6121 ExplodeDelay[x][y] = last_phase;
6126 if (center_element == EL_YAMYAM)
6127 game.yamyam_content_nr =
6128 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6140 GfxFrame[x][y] = 0; /* restart explosion animation */
6142 last_phase = ExplodeDelay[x][y];
6144 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6148 /* activate this even in non-DEBUG version until cause for crash in
6149 getGraphicAnimationFrame() (see below) is found and eliminated */
6155 /* this can happen if the player leaves an explosion just in time */
6156 if (GfxElement[x][y] == EL_UNDEFINED)
6157 GfxElement[x][y] = EL_EMPTY;
6159 if (GfxElement[x][y] == EL_UNDEFINED)
6162 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
6163 printf("Explode(): This should never happen!\n");
6166 GfxElement[x][y] = EL_EMPTY;
6172 border_element = Store2[x][y];
6173 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6174 border_element = StorePlayer[x][y];
6176 if (phase == element_info[border_element].ignition_delay ||
6177 phase == last_phase)
6179 boolean border_explosion = FALSE;
6181 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6182 !PLAYER_EXPLOSION_PROTECTED(x, y))
6184 KillPlayerUnlessExplosionProtected(x, y);
6185 border_explosion = TRUE;
6187 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6189 Feld[x][y] = Store2[x][y];
6192 border_explosion = TRUE;
6194 else if (border_element == EL_AMOEBA_TO_DIAMOND)
6196 AmoebeUmwandeln(x, y);
6198 border_explosion = TRUE;
6201 /* if an element just explodes due to another explosion (chain-reaction),
6202 do not immediately end the new explosion when it was the last frame of
6203 the explosion (as it would be done in the following "if"-statement!) */
6204 if (border_explosion && phase == last_phase)
6208 if (phase == last_phase)
6212 element = Feld[x][y] = Store[x][y];
6213 Store[x][y] = Store2[x][y] = 0;
6214 GfxElement[x][y] = EL_UNDEFINED;
6216 /* player can escape from explosions and might therefore be still alive */
6217 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6218 element <= EL_PLAYER_IS_EXPLODING_4)
6220 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6221 int explosion_element = EL_PLAYER_1 + player_nr;
6222 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6223 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6225 if (level.use_explosion_element[player_nr])
6226 explosion_element = level.explosion_element[player_nr];
6228 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6229 element_info[explosion_element].content.e[xx][yy]);
6232 /* restore probably existing indestructible background element */
6233 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6234 element = Feld[x][y] = Back[x][y];
6237 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6238 GfxDir[x][y] = MV_NONE;
6239 ChangeDelay[x][y] = 0;
6240 ChangePage[x][y] = -1;
6242 #if USE_NEW_CUSTOM_VALUE
6243 CustomValue[x][y] = 0;
6246 InitField_WithBug2(x, y, FALSE);
6248 TEST_DrawLevelField(x, y);
6250 TestIfElementTouchesCustomElement(x, y);
6252 if (GFX_CRUMBLED(element))
6253 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6255 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6256 StorePlayer[x][y] = 0;
6258 if (ELEM_IS_PLAYER(element))
6259 RelocatePlayer(x, y, element);
6261 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6263 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6264 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6267 TEST_DrawLevelFieldCrumbled(x, y);
6269 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6271 DrawLevelElement(x, y, Back[x][y]);
6272 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6274 else if (IS_WALKABLE_UNDER(Back[x][y]))
6276 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6277 DrawLevelElementThruMask(x, y, Back[x][y]);
6279 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6280 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6284 void DynaExplode(int ex, int ey)
6287 int dynabomb_element = Feld[ex][ey];
6288 int dynabomb_size = 1;
6289 boolean dynabomb_xl = FALSE;
6290 struct PlayerInfo *player;
6291 static int xy[4][2] =
6299 if (IS_ACTIVE_BOMB(dynabomb_element))
6301 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6302 dynabomb_size = player->dynabomb_size;
6303 dynabomb_xl = player->dynabomb_xl;
6304 player->dynabombs_left++;
6307 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6309 for (i = 0; i < NUM_DIRECTIONS; i++)
6311 for (j = 1; j <= dynabomb_size; j++)
6313 int x = ex + j * xy[i][0];
6314 int y = ey + j * xy[i][1];
6317 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6320 element = Feld[x][y];
6322 /* do not restart explosions of fields with active bombs */
6323 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6326 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6328 if (element != EL_EMPTY && element != EL_EXPLOSION &&
6329 !IS_DIGGABLE(element) && !dynabomb_xl)
6335 void Bang(int x, int y)
6337 int element = MovingOrBlocked2Element(x, y);
6338 int explosion_type = EX_TYPE_NORMAL;
6340 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6342 struct PlayerInfo *player = PLAYERINFO(x, y);
6344 #if USE_FIX_CE_ACTION_WITH_PLAYER
6345 element = Feld[x][y] = player->initial_element;
6347 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6348 player->element_nr);
6351 if (level.use_explosion_element[player->index_nr])
6353 int explosion_element = level.explosion_element[player->index_nr];
6355 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6356 explosion_type = EX_TYPE_CROSS;
6357 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6358 explosion_type = EX_TYPE_CENTER;
6366 case EL_BD_BUTTERFLY:
6369 case EL_DARK_YAMYAM:
6373 RaiseScoreElement(element);
6376 case EL_DYNABOMB_PLAYER_1_ACTIVE:
6377 case EL_DYNABOMB_PLAYER_2_ACTIVE:
6378 case EL_DYNABOMB_PLAYER_3_ACTIVE:
6379 case EL_DYNABOMB_PLAYER_4_ACTIVE:
6380 case EL_DYNABOMB_INCREASE_NUMBER:
6381 case EL_DYNABOMB_INCREASE_SIZE:
6382 case EL_DYNABOMB_INCREASE_POWER:
6383 explosion_type = EX_TYPE_DYNA;
6386 case EL_DC_LANDMINE:
6388 case EL_EM_EXIT_OPEN:
6389 case EL_EM_STEEL_EXIT_OPEN:
6391 explosion_type = EX_TYPE_CENTER;
6396 case EL_LAMP_ACTIVE:
6397 case EL_AMOEBA_TO_DIAMOND:
6398 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
6399 explosion_type = EX_TYPE_CENTER;
6403 if (element_info[element].explosion_type == EXPLODES_CROSS)
6404 explosion_type = EX_TYPE_CROSS;
6405 else if (element_info[element].explosion_type == EXPLODES_1X1)
6406 explosion_type = EX_TYPE_CENTER;
6410 if (explosion_type == EX_TYPE_DYNA)
6413 Explode(x, y, EX_PHASE_START, explosion_type);
6415 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6418 void SplashAcid(int x, int y)
6420 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6421 (!IN_LEV_FIELD(x - 1, y - 2) ||
6422 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6423 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6425 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6426 (!IN_LEV_FIELD(x + 1, y - 2) ||
6427 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6428 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6430 PlayLevelSound(x, y, SND_ACID_SPLASHING);
6433 static void InitBeltMovement()
6435 static int belt_base_element[4] =
6437 EL_CONVEYOR_BELT_1_LEFT,
6438 EL_CONVEYOR_BELT_2_LEFT,
6439 EL_CONVEYOR_BELT_3_LEFT,
6440 EL_CONVEYOR_BELT_4_LEFT
6442 static int belt_base_active_element[4] =
6444 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6445 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6446 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6447 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6452 /* set frame order for belt animation graphic according to belt direction */
6453 for (i = 0; i < NUM_BELTS; i++)
6457 for (j = 0; j < NUM_BELT_PARTS; j++)
6459 int element = belt_base_active_element[belt_nr] + j;
6460 int graphic_1 = el2img(element);
6461 int graphic_2 = el2panelimg(element);
6463 if (game.belt_dir[i] == MV_LEFT)
6465 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6466 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6470 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6471 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6476 SCAN_PLAYFIELD(x, y)
6478 int element = Feld[x][y];
6480 for (i = 0; i < NUM_BELTS; i++)
6482 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6484 int e_belt_nr = getBeltNrFromBeltElement(element);
6487 if (e_belt_nr == belt_nr)
6489 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6491 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6498 static void ToggleBeltSwitch(int x, int y)
6500 static int belt_base_element[4] =
6502 EL_CONVEYOR_BELT_1_LEFT,
6503 EL_CONVEYOR_BELT_2_LEFT,
6504 EL_CONVEYOR_BELT_3_LEFT,
6505 EL_CONVEYOR_BELT_4_LEFT
6507 static int belt_base_active_element[4] =
6509 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6510 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6511 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6512 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6514 static int belt_base_switch_element[4] =
6516 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6517 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6518 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6519 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6521 static int belt_move_dir[4] =
6529 int element = Feld[x][y];
6530 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6531 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6532 int belt_dir = belt_move_dir[belt_dir_nr];
6535 if (!IS_BELT_SWITCH(element))
6538 game.belt_dir_nr[belt_nr] = belt_dir_nr;
6539 game.belt_dir[belt_nr] = belt_dir;
6541 if (belt_dir_nr == 3)
6544 /* set frame order for belt animation graphic according to belt direction */
6545 for (i = 0; i < NUM_BELT_PARTS; i++)
6547 int element = belt_base_active_element[belt_nr] + i;
6548 int graphic_1 = el2img(element);
6549 int graphic_2 = el2panelimg(element);
6551 if (belt_dir == MV_LEFT)
6553 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6554 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6558 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6559 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6563 SCAN_PLAYFIELD(xx, yy)
6565 int element = Feld[xx][yy];
6567 if (IS_BELT_SWITCH(element))
6569 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6571 if (e_belt_nr == belt_nr)
6573 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6574 TEST_DrawLevelField(xx, yy);
6577 else if (IS_BELT(element) && belt_dir != MV_NONE)
6579 int e_belt_nr = getBeltNrFromBeltElement(element);
6581 if (e_belt_nr == belt_nr)
6583 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6585 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6586 TEST_DrawLevelField(xx, yy);
6589 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6591 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6593 if (e_belt_nr == belt_nr)
6595 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6597 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6598 TEST_DrawLevelField(xx, yy);
6604 static void ToggleSwitchgateSwitch(int x, int y)
6608 game.switchgate_pos = !game.switchgate_pos;
6610 SCAN_PLAYFIELD(xx, yy)
6612 int element = Feld[xx][yy];
6614 #if !USE_BOTH_SWITCHGATE_SWITCHES
6615 if (element == EL_SWITCHGATE_SWITCH_UP ||
6616 element == EL_SWITCHGATE_SWITCH_DOWN)
6618 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6619 TEST_DrawLevelField(xx, yy);
6621 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6622 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6624 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6625 TEST_DrawLevelField(xx, yy);
6628 if (element == EL_SWITCHGATE_SWITCH_UP)
6630 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6631 TEST_DrawLevelField(xx, yy);
6633 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6635 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6636 TEST_DrawLevelField(xx, yy);
6638 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6640 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6641 TEST_DrawLevelField(xx, yy);
6643 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6645 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6646 TEST_DrawLevelField(xx, yy);
6649 else if (element == EL_SWITCHGATE_OPEN ||
6650 element == EL_SWITCHGATE_OPENING)
6652 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6654 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6656 else if (element == EL_SWITCHGATE_CLOSED ||
6657 element == EL_SWITCHGATE_CLOSING)
6659 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6661 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6666 static int getInvisibleActiveFromInvisibleElement(int element)
6668 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6669 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6670 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6674 static int getInvisibleFromInvisibleActiveElement(int element)
6676 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6677 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6678 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6682 static void RedrawAllLightSwitchesAndInvisibleElements()
6686 SCAN_PLAYFIELD(x, y)
6688 int element = Feld[x][y];
6690 if (element == EL_LIGHT_SWITCH &&
6691 game.light_time_left > 0)
6693 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6694 TEST_DrawLevelField(x, y);
6696 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6697 game.light_time_left == 0)
6699 Feld[x][y] = EL_LIGHT_SWITCH;
6700 TEST_DrawLevelField(x, y);
6702 else if (element == EL_EMC_DRIPPER &&
6703 game.light_time_left > 0)
6705 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6706 TEST_DrawLevelField(x, y);
6708 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6709 game.light_time_left == 0)
6711 Feld[x][y] = EL_EMC_DRIPPER;
6712 TEST_DrawLevelField(x, y);
6714 else if (element == EL_INVISIBLE_STEELWALL ||
6715 element == EL_INVISIBLE_WALL ||
6716 element == EL_INVISIBLE_SAND)
6718 if (game.light_time_left > 0)
6719 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6721 TEST_DrawLevelField(x, y);
6723 /* uncrumble neighbour fields, if needed */
6724 if (element == EL_INVISIBLE_SAND)
6725 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6727 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6728 element == EL_INVISIBLE_WALL_ACTIVE ||
6729 element == EL_INVISIBLE_SAND_ACTIVE)
6731 if (game.light_time_left == 0)
6732 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6734 TEST_DrawLevelField(x, y);
6736 /* re-crumble neighbour fields, if needed */
6737 if (element == EL_INVISIBLE_SAND)
6738 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6743 static void RedrawAllInvisibleElementsForLenses()
6747 SCAN_PLAYFIELD(x, y)
6749 int element = Feld[x][y];
6751 if (element == EL_EMC_DRIPPER &&
6752 game.lenses_time_left > 0)
6754 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6755 TEST_DrawLevelField(x, y);
6757 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6758 game.lenses_time_left == 0)
6760 Feld[x][y] = EL_EMC_DRIPPER;
6761 TEST_DrawLevelField(x, y);
6763 else if (element == EL_INVISIBLE_STEELWALL ||
6764 element == EL_INVISIBLE_WALL ||
6765 element == EL_INVISIBLE_SAND)
6767 if (game.lenses_time_left > 0)
6768 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6770 TEST_DrawLevelField(x, y);
6772 /* uncrumble neighbour fields, if needed */
6773 if (element == EL_INVISIBLE_SAND)
6774 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6776 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6777 element == EL_INVISIBLE_WALL_ACTIVE ||
6778 element == EL_INVISIBLE_SAND_ACTIVE)
6780 if (game.lenses_time_left == 0)
6781 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6783 TEST_DrawLevelField(x, y);
6785 /* re-crumble neighbour fields, if needed */
6786 if (element == EL_INVISIBLE_SAND)
6787 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6792 static void RedrawAllInvisibleElementsForMagnifier()
6796 SCAN_PLAYFIELD(x, y)
6798 int element = Feld[x][y];
6800 if (element == EL_EMC_FAKE_GRASS &&
6801 game.magnify_time_left > 0)
6803 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6804 TEST_DrawLevelField(x, y);
6806 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6807 game.magnify_time_left == 0)
6809 Feld[x][y] = EL_EMC_FAKE_GRASS;
6810 TEST_DrawLevelField(x, y);
6812 else if (IS_GATE_GRAY(element) &&
6813 game.magnify_time_left > 0)
6815 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6816 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6817 IS_EM_GATE_GRAY(element) ?
6818 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6819 IS_EMC_GATE_GRAY(element) ?
6820 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6821 IS_DC_GATE_GRAY(element) ?
6822 EL_DC_GATE_WHITE_GRAY_ACTIVE :
6824 TEST_DrawLevelField(x, y);
6826 else if (IS_GATE_GRAY_ACTIVE(element) &&
6827 game.magnify_time_left == 0)
6829 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6830 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6831 IS_EM_GATE_GRAY_ACTIVE(element) ?
6832 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6833 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6834 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6835 IS_DC_GATE_GRAY_ACTIVE(element) ?
6836 EL_DC_GATE_WHITE_GRAY :
6838 TEST_DrawLevelField(x, y);
6843 static void ToggleLightSwitch(int x, int y)
6845 int element = Feld[x][y];
6847 game.light_time_left =
6848 (element == EL_LIGHT_SWITCH ?
6849 level.time_light * FRAMES_PER_SECOND : 0);
6851 RedrawAllLightSwitchesAndInvisibleElements();
6854 static void ActivateTimegateSwitch(int x, int y)
6858 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6860 SCAN_PLAYFIELD(xx, yy)
6862 int element = Feld[xx][yy];
6864 if (element == EL_TIMEGATE_CLOSED ||
6865 element == EL_TIMEGATE_CLOSING)
6867 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6868 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6872 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6874 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6875 TEST_DrawLevelField(xx, yy);
6882 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6883 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6885 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6889 void Impact(int x, int y)
6891 boolean last_line = (y == lev_fieldy - 1);
6892 boolean object_hit = FALSE;
6893 boolean impact = (last_line || object_hit);
6894 int element = Feld[x][y];
6895 int smashed = EL_STEELWALL;
6897 if (!last_line) /* check if element below was hit */
6899 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6902 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6903 MovDir[x][y + 1] != MV_DOWN ||
6904 MovPos[x][y + 1] <= TILEY / 2));
6906 /* do not smash moving elements that left the smashed field in time */
6907 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6908 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6911 #if USE_QUICKSAND_IMPACT_BUGFIX
6912 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6914 RemoveMovingField(x, y + 1);
6915 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6916 Feld[x][y + 2] = EL_ROCK;
6917 TEST_DrawLevelField(x, y + 2);
6922 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6924 RemoveMovingField(x, y + 1);
6925 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6926 Feld[x][y + 2] = EL_ROCK;
6927 TEST_DrawLevelField(x, y + 2);
6934 smashed = MovingOrBlocked2Element(x, y + 1);
6936 impact = (last_line || object_hit);
6939 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6941 SplashAcid(x, y + 1);
6945 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6946 /* only reset graphic animation if graphic really changes after impact */
6948 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6950 ResetGfxAnimation(x, y);
6951 TEST_DrawLevelField(x, y);
6954 if (impact && CAN_EXPLODE_IMPACT(element))
6959 else if (impact && element == EL_PEARL &&
6960 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6962 ResetGfxAnimation(x, y);
6964 Feld[x][y] = EL_PEARL_BREAKING;
6965 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6968 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6970 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6975 if (impact && element == EL_AMOEBA_DROP)
6977 if (object_hit && IS_PLAYER(x, y + 1))
6978 KillPlayerUnlessEnemyProtected(x, y + 1);
6979 else if (object_hit && smashed == EL_PENGUIN)
6983 Feld[x][y] = EL_AMOEBA_GROWING;
6984 Store[x][y] = EL_AMOEBA_WET;
6986 ResetRandomAnimationValue(x, y);
6991 if (object_hit) /* check which object was hit */
6993 if ((CAN_PASS_MAGIC_WALL(element) &&
6994 (smashed == EL_MAGIC_WALL ||
6995 smashed == EL_BD_MAGIC_WALL)) ||
6996 (CAN_PASS_DC_MAGIC_WALL(element) &&
6997 smashed == EL_DC_MAGIC_WALL))
7000 int activated_magic_wall =
7001 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
7002 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
7003 EL_DC_MAGIC_WALL_ACTIVE);
7005 /* activate magic wall / mill */
7006 SCAN_PLAYFIELD(xx, yy)
7008 if (Feld[xx][yy] == smashed)
7009 Feld[xx][yy] = activated_magic_wall;
7012 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
7013 game.magic_wall_active = TRUE;
7015 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
7016 SND_MAGIC_WALL_ACTIVATING :
7017 smashed == EL_BD_MAGIC_WALL ?
7018 SND_BD_MAGIC_WALL_ACTIVATING :
7019 SND_DC_MAGIC_WALL_ACTIVATING));
7022 if (IS_PLAYER(x, y + 1))
7024 if (CAN_SMASH_PLAYER(element))
7026 KillPlayerUnlessEnemyProtected(x, y + 1);
7030 else if (smashed == EL_PENGUIN)
7032 if (CAN_SMASH_PLAYER(element))
7038 else if (element == EL_BD_DIAMOND)
7040 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
7046 else if (((element == EL_SP_INFOTRON ||
7047 element == EL_SP_ZONK) &&
7048 (smashed == EL_SP_SNIKSNAK ||
7049 smashed == EL_SP_ELECTRON ||
7050 smashed == EL_SP_DISK_ORANGE)) ||
7051 (element == EL_SP_INFOTRON &&
7052 smashed == EL_SP_DISK_YELLOW))
7057 else if (CAN_SMASH_EVERYTHING(element))
7059 if (IS_CLASSIC_ENEMY(smashed) ||
7060 CAN_EXPLODE_SMASHED(smashed))
7065 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
7067 if (smashed == EL_LAMP ||
7068 smashed == EL_LAMP_ACTIVE)
7073 else if (smashed == EL_NUT)
7075 Feld[x][y + 1] = EL_NUT_BREAKING;
7076 PlayLevelSound(x, y, SND_NUT_BREAKING);
7077 RaiseScoreElement(EL_NUT);
7080 else if (smashed == EL_PEARL)
7082 ResetGfxAnimation(x, y);
7084 Feld[x][y + 1] = EL_PEARL_BREAKING;
7085 PlayLevelSound(x, y, SND_PEARL_BREAKING);
7088 else if (smashed == EL_DIAMOND)
7090 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
7091 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
7094 else if (IS_BELT_SWITCH(smashed))
7096 ToggleBeltSwitch(x, y + 1);
7098 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
7099 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
7100 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
7101 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
7103 ToggleSwitchgateSwitch(x, y + 1);
7105 else if (smashed == EL_LIGHT_SWITCH ||
7106 smashed == EL_LIGHT_SWITCH_ACTIVE)
7108 ToggleLightSwitch(x, y + 1);
7113 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
7116 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7118 CheckElementChangeBySide(x, y + 1, smashed, element,
7119 CE_SWITCHED, CH_SIDE_TOP);
7120 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7126 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7131 /* play sound of magic wall / mill */
7133 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7134 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7135 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7137 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7138 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7139 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7140 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7141 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7142 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7147 /* play sound of object that hits the ground */
7148 if (last_line || object_hit)
7149 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7152 inline static void TurnRoundExt(int x, int y)
7164 { 0, 0 }, { 0, 0 }, { 0, 0 },
7169 int left, right, back;
7173 { MV_DOWN, MV_UP, MV_RIGHT },
7174 { MV_UP, MV_DOWN, MV_LEFT },
7176 { MV_LEFT, MV_RIGHT, MV_DOWN },
7180 { MV_RIGHT, MV_LEFT, MV_UP }
7183 int element = Feld[x][y];
7184 int move_pattern = element_info[element].move_pattern;
7186 int old_move_dir = MovDir[x][y];
7187 int left_dir = turn[old_move_dir].left;
7188 int right_dir = turn[old_move_dir].right;
7189 int back_dir = turn[old_move_dir].back;
7191 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
7192 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
7193 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
7194 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
7196 int left_x = x + left_dx, left_y = y + left_dy;
7197 int right_x = x + right_dx, right_y = y + right_dy;
7198 int move_x = x + move_dx, move_y = y + move_dy;
7202 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7204 TestIfBadThingTouchesOtherBadThing(x, y);
7206 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7207 MovDir[x][y] = right_dir;
7208 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7209 MovDir[x][y] = left_dir;
7211 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7213 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
7216 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7218 TestIfBadThingTouchesOtherBadThing(x, y);
7220 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7221 MovDir[x][y] = left_dir;
7222 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7223 MovDir[x][y] = right_dir;
7225 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7227 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
7230 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7232 TestIfBadThingTouchesOtherBadThing(x, y);
7234 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7235 MovDir[x][y] = left_dir;
7236 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7237 MovDir[x][y] = right_dir;
7239 if (MovDir[x][y] != old_move_dir)
7242 else if (element == EL_YAMYAM)
7244 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7245 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7247 if (can_turn_left && can_turn_right)
7248 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7249 else if (can_turn_left)
7250 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7251 else if (can_turn_right)
7252 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7254 MovDir[x][y] = back_dir;
7256 MovDelay[x][y] = 16 + 16 * RND(3);
7258 else if (element == EL_DARK_YAMYAM)
7260 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7262 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7265 if (can_turn_left && can_turn_right)
7266 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7267 else if (can_turn_left)
7268 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7269 else if (can_turn_right)
7270 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7272 MovDir[x][y] = back_dir;
7274 MovDelay[x][y] = 16 + 16 * RND(3);
7276 else if (element == EL_PACMAN)
7278 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7279 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7281 if (can_turn_left && can_turn_right)
7282 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7283 else if (can_turn_left)
7284 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7285 else if (can_turn_right)
7286 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7288 MovDir[x][y] = back_dir;
7290 MovDelay[x][y] = 6 + RND(40);
7292 else if (element == EL_PIG)
7294 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7295 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7296 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7297 boolean should_turn_left, should_turn_right, should_move_on;
7299 int rnd = RND(rnd_value);
7301 should_turn_left = (can_turn_left &&
7303 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7304 y + back_dy + left_dy)));
7305 should_turn_right = (can_turn_right &&
7307 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7308 y + back_dy + right_dy)));
7309 should_move_on = (can_move_on &&
7312 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7313 y + move_dy + left_dy) ||
7314 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7315 y + move_dy + right_dy)));
7317 if (should_turn_left || should_turn_right || should_move_on)
7319 if (should_turn_left && should_turn_right && should_move_on)
7320 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
7321 rnd < 2 * rnd_value / 3 ? right_dir :
7323 else if (should_turn_left && should_turn_right)
7324 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7325 else if (should_turn_left && should_move_on)
7326 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7327 else if (should_turn_right && should_move_on)
7328 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7329 else if (should_turn_left)
7330 MovDir[x][y] = left_dir;
7331 else if (should_turn_right)
7332 MovDir[x][y] = right_dir;
7333 else if (should_move_on)
7334 MovDir[x][y] = old_move_dir;
7336 else if (can_move_on && rnd > rnd_value / 8)
7337 MovDir[x][y] = old_move_dir;
7338 else if (can_turn_left && can_turn_right)
7339 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7340 else if (can_turn_left && rnd > rnd_value / 8)
7341 MovDir[x][y] = left_dir;
7342 else if (can_turn_right && rnd > rnd_value/8)
7343 MovDir[x][y] = right_dir;
7345 MovDir[x][y] = back_dir;
7347 xx = x + move_xy[MovDir[x][y]].dx;
7348 yy = y + move_xy[MovDir[x][y]].dy;
7350 if (!IN_LEV_FIELD(xx, yy) ||
7351 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7352 MovDir[x][y] = old_move_dir;
7356 else if (element == EL_DRAGON)
7358 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7359 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7360 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7362 int rnd = RND(rnd_value);
7364 if (can_move_on && rnd > rnd_value / 8)
7365 MovDir[x][y] = old_move_dir;
7366 else if (can_turn_left && can_turn_right)
7367 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7368 else if (can_turn_left && rnd > rnd_value / 8)
7369 MovDir[x][y] = left_dir;
7370 else if (can_turn_right && rnd > rnd_value / 8)
7371 MovDir[x][y] = right_dir;
7373 MovDir[x][y] = back_dir;
7375 xx = x + move_xy[MovDir[x][y]].dx;
7376 yy = y + move_xy[MovDir[x][y]].dy;
7378 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7379 MovDir[x][y] = old_move_dir;
7383 else if (element == EL_MOLE)
7385 boolean can_move_on =
7386 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7387 IS_AMOEBOID(Feld[move_x][move_y]) ||
7388 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7391 boolean can_turn_left =
7392 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7393 IS_AMOEBOID(Feld[left_x][left_y])));
7395 boolean can_turn_right =
7396 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7397 IS_AMOEBOID(Feld[right_x][right_y])));
7399 if (can_turn_left && can_turn_right)
7400 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7401 else if (can_turn_left)
7402 MovDir[x][y] = left_dir;
7404 MovDir[x][y] = right_dir;
7407 if (MovDir[x][y] != old_move_dir)
7410 else if (element == EL_BALLOON)
7412 MovDir[x][y] = game.wind_direction;
7415 else if (element == EL_SPRING)
7417 #if USE_NEW_SPRING_BUMPER
7418 if (MovDir[x][y] & MV_HORIZONTAL)
7420 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7421 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7423 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7424 ResetGfxAnimation(move_x, move_y);
7425 TEST_DrawLevelField(move_x, move_y);
7427 MovDir[x][y] = back_dir;
7429 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7430 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7431 MovDir[x][y] = MV_NONE;
7434 if (MovDir[x][y] & MV_HORIZONTAL &&
7435 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7436 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7437 MovDir[x][y] = MV_NONE;
7442 else if (element == EL_ROBOT ||
7443 element == EL_SATELLITE ||
7444 element == EL_PENGUIN ||
7445 element == EL_EMC_ANDROID)
7447 int attr_x = -1, attr_y = -1;
7458 for (i = 0; i < MAX_PLAYERS; i++)
7460 struct PlayerInfo *player = &stored_player[i];
7461 int jx = player->jx, jy = player->jy;
7463 if (!player->active)
7467 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7475 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7476 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7477 game.engine_version < VERSION_IDENT(3,1,0,0)))
7483 if (element == EL_PENGUIN)
7486 static int xy[4][2] =
7494 for (i = 0; i < NUM_DIRECTIONS; i++)
7496 int ex = x + xy[i][0];
7497 int ey = y + xy[i][1];
7499 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7500 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7501 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7502 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7511 MovDir[x][y] = MV_NONE;
7513 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7514 else if (attr_x > x)
7515 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7517 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7518 else if (attr_y > y)
7519 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7521 if (element == EL_ROBOT)
7525 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7526 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7527 Moving2Blocked(x, y, &newx, &newy);
7529 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7530 MovDelay[x][y] = 8 + 8 * !RND(3);
7532 MovDelay[x][y] = 16;
7534 else if (element == EL_PENGUIN)
7540 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7542 boolean first_horiz = RND(2);
7543 int new_move_dir = MovDir[x][y];
7546 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7547 Moving2Blocked(x, y, &newx, &newy);
7549 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7553 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7554 Moving2Blocked(x, y, &newx, &newy);
7556 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7559 MovDir[x][y] = old_move_dir;
7563 else if (element == EL_SATELLITE)
7569 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7571 boolean first_horiz = RND(2);
7572 int new_move_dir = MovDir[x][y];
7575 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7576 Moving2Blocked(x, y, &newx, &newy);
7578 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7582 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7583 Moving2Blocked(x, y, &newx, &newy);
7585 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7588 MovDir[x][y] = old_move_dir;
7592 else if (element == EL_EMC_ANDROID)
7594 static int check_pos[16] =
7596 -1, /* 0 => (invalid) */
7597 7, /* 1 => MV_LEFT */
7598 3, /* 2 => MV_RIGHT */
7599 -1, /* 3 => (invalid) */
7601 0, /* 5 => MV_LEFT | MV_UP */
7602 2, /* 6 => MV_RIGHT | MV_UP */
7603 -1, /* 7 => (invalid) */
7604 5, /* 8 => MV_DOWN */
7605 6, /* 9 => MV_LEFT | MV_DOWN */
7606 4, /* 10 => MV_RIGHT | MV_DOWN */
7607 -1, /* 11 => (invalid) */
7608 -1, /* 12 => (invalid) */
7609 -1, /* 13 => (invalid) */
7610 -1, /* 14 => (invalid) */
7611 -1, /* 15 => (invalid) */
7619 { -1, -1, MV_LEFT | MV_UP },
7621 { +1, -1, MV_RIGHT | MV_UP },
7622 { +1, 0, MV_RIGHT },
7623 { +1, +1, MV_RIGHT | MV_DOWN },
7625 { -1, +1, MV_LEFT | MV_DOWN },
7628 int start_pos, check_order;
7629 boolean can_clone = FALSE;
7632 /* check if there is any free field around current position */
7633 for (i = 0; i < 8; i++)
7635 int newx = x + check_xy[i].dx;
7636 int newy = y + check_xy[i].dy;
7638 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7646 if (can_clone) /* randomly find an element to clone */
7650 start_pos = check_pos[RND(8)];
7651 check_order = (RND(2) ? -1 : +1);
7653 for (i = 0; i < 8; i++)
7655 int pos_raw = start_pos + i * check_order;
7656 int pos = (pos_raw + 8) % 8;
7657 int newx = x + check_xy[pos].dx;
7658 int newy = y + check_xy[pos].dy;
7660 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7662 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7663 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7665 Store[x][y] = Feld[newx][newy];
7674 if (can_clone) /* randomly find a direction to move */
7678 start_pos = check_pos[RND(8)];
7679 check_order = (RND(2) ? -1 : +1);
7681 for (i = 0; i < 8; i++)
7683 int pos_raw = start_pos + i * check_order;
7684 int pos = (pos_raw + 8) % 8;
7685 int newx = x + check_xy[pos].dx;
7686 int newy = y + check_xy[pos].dy;
7687 int new_move_dir = check_xy[pos].dir;
7689 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7691 MovDir[x][y] = new_move_dir;
7692 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7701 if (can_clone) /* cloning and moving successful */
7704 /* cannot clone -- try to move towards player */
7706 start_pos = check_pos[MovDir[x][y] & 0x0f];
7707 check_order = (RND(2) ? -1 : +1);
7709 for (i = 0; i < 3; i++)
7711 /* first check start_pos, then previous/next or (next/previous) pos */
7712 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7713 int pos = (pos_raw + 8) % 8;
7714 int newx = x + check_xy[pos].dx;
7715 int newy = y + check_xy[pos].dy;
7716 int new_move_dir = check_xy[pos].dir;
7718 if (IS_PLAYER(newx, newy))
7721 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7723 MovDir[x][y] = new_move_dir;
7724 MovDelay[x][y] = level.android_move_time * 8 + 1;
7731 else if (move_pattern == MV_TURNING_LEFT ||
7732 move_pattern == MV_TURNING_RIGHT ||
7733 move_pattern == MV_TURNING_LEFT_RIGHT ||
7734 move_pattern == MV_TURNING_RIGHT_LEFT ||
7735 move_pattern == MV_TURNING_RANDOM ||
7736 move_pattern == MV_ALL_DIRECTIONS)
7738 boolean can_turn_left =
7739 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7740 boolean can_turn_right =
7741 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7743 if (element_info[element].move_stepsize == 0) /* "not moving" */
7746 if (move_pattern == MV_TURNING_LEFT)
7747 MovDir[x][y] = left_dir;
7748 else if (move_pattern == MV_TURNING_RIGHT)
7749 MovDir[x][y] = right_dir;
7750 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7751 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7752 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7753 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7754 else if (move_pattern == MV_TURNING_RANDOM)
7755 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7756 can_turn_right && !can_turn_left ? right_dir :
7757 RND(2) ? left_dir : right_dir);
7758 else if (can_turn_left && can_turn_right)
7759 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7760 else if (can_turn_left)
7761 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7762 else if (can_turn_right)
7763 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7765 MovDir[x][y] = back_dir;
7767 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7769 else if (move_pattern == MV_HORIZONTAL ||
7770 move_pattern == MV_VERTICAL)
7772 if (move_pattern & old_move_dir)
7773 MovDir[x][y] = back_dir;
7774 else if (move_pattern == MV_HORIZONTAL)
7775 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7776 else if (move_pattern == MV_VERTICAL)
7777 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7779 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7781 else if (move_pattern & MV_ANY_DIRECTION)
7783 MovDir[x][y] = move_pattern;
7784 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7786 else if (move_pattern & MV_WIND_DIRECTION)
7788 MovDir[x][y] = game.wind_direction;
7789 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7791 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7793 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7794 MovDir[x][y] = left_dir;
7795 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7796 MovDir[x][y] = right_dir;
7798 if (MovDir[x][y] != old_move_dir)
7799 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7801 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7803 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7804 MovDir[x][y] = right_dir;
7805 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7806 MovDir[x][y] = left_dir;
7808 if (MovDir[x][y] != old_move_dir)
7809 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7811 else if (move_pattern == MV_TOWARDS_PLAYER ||
7812 move_pattern == MV_AWAY_FROM_PLAYER)
7814 int attr_x = -1, attr_y = -1;
7816 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7827 for (i = 0; i < MAX_PLAYERS; i++)
7829 struct PlayerInfo *player = &stored_player[i];
7830 int jx = player->jx, jy = player->jy;
7832 if (!player->active)
7836 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7844 MovDir[x][y] = MV_NONE;
7846 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7847 else if (attr_x > x)
7848 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7850 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7851 else if (attr_y > y)
7852 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7854 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7856 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7858 boolean first_horiz = RND(2);
7859 int new_move_dir = MovDir[x][y];
7861 if (element_info[element].move_stepsize == 0) /* "not moving" */
7863 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7864 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7870 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7871 Moving2Blocked(x, y, &newx, &newy);
7873 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7877 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7878 Moving2Blocked(x, y, &newx, &newy);
7880 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7883 MovDir[x][y] = old_move_dir;
7886 else if (move_pattern == MV_WHEN_PUSHED ||
7887 move_pattern == MV_WHEN_DROPPED)
7889 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7890 MovDir[x][y] = MV_NONE;
7894 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7896 static int test_xy[7][2] =
7906 static int test_dir[7] =
7916 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7917 int move_preference = -1000000; /* start with very low preference */
7918 int new_move_dir = MV_NONE;
7919 int start_test = RND(4);
7922 for (i = 0; i < NUM_DIRECTIONS; i++)
7924 int move_dir = test_dir[start_test + i];
7925 int move_dir_preference;
7927 xx = x + test_xy[start_test + i][0];
7928 yy = y + test_xy[start_test + i][1];
7930 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7931 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7933 new_move_dir = move_dir;
7938 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7941 move_dir_preference = -1 * RunnerVisit[xx][yy];
7942 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7943 move_dir_preference = PlayerVisit[xx][yy];
7945 if (move_dir_preference > move_preference)
7947 /* prefer field that has not been visited for the longest time */
7948 move_preference = move_dir_preference;
7949 new_move_dir = move_dir;
7951 else if (move_dir_preference == move_preference &&
7952 move_dir == old_move_dir)
7954 /* prefer last direction when all directions are preferred equally */
7955 move_preference = move_dir_preference;
7956 new_move_dir = move_dir;
7960 MovDir[x][y] = new_move_dir;
7961 if (old_move_dir != new_move_dir)
7962 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7966 static void TurnRound(int x, int y)
7968 int direction = MovDir[x][y];
7972 GfxDir[x][y] = MovDir[x][y];
7974 if (direction != MovDir[x][y])
7978 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7980 ResetGfxFrame(x, y, FALSE);
7983 static boolean JustBeingPushed(int x, int y)
7987 for (i = 0; i < MAX_PLAYERS; i++)
7989 struct PlayerInfo *player = &stored_player[i];
7991 if (player->active && player->is_pushing && player->MovPos)
7993 int next_jx = player->jx + (player->jx - player->last_jx);
7994 int next_jy = player->jy + (player->jy - player->last_jy);
7996 if (x == next_jx && y == next_jy)
8004 void StartMoving(int x, int y)
8006 boolean started_moving = FALSE; /* some elements can fall _and_ move */
8007 int element = Feld[x][y];
8012 if (MovDelay[x][y] == 0)
8013 GfxAction[x][y] = ACTION_DEFAULT;
8015 if (CAN_FALL(element) && y < lev_fieldy - 1)
8017 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
8018 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
8019 if (JustBeingPushed(x, y))
8022 if (element == EL_QUICKSAND_FULL)
8024 if (IS_FREE(x, y + 1))
8026 InitMovingField(x, y, MV_DOWN);
8027 started_moving = TRUE;
8029 Feld[x][y] = EL_QUICKSAND_EMPTYING;
8030 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8031 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8032 Store[x][y] = EL_ROCK;
8034 Store[x][y] = EL_ROCK;
8037 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8039 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8041 if (!MovDelay[x][y])
8043 MovDelay[x][y] = TILEY + 1;
8045 ResetGfxAnimation(x, y);
8046 ResetGfxAnimation(x, y + 1);
8051 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
8052 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8059 Feld[x][y] = EL_QUICKSAND_EMPTY;
8060 Feld[x][y + 1] = EL_QUICKSAND_FULL;
8061 Store[x][y + 1] = Store[x][y];
8064 PlayLevelSoundAction(x, y, ACTION_FILLING);
8066 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8068 if (!MovDelay[x][y])
8070 MovDelay[x][y] = TILEY + 1;
8072 ResetGfxAnimation(x, y);
8073 ResetGfxAnimation(x, y + 1);
8078 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
8079 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8086 Feld[x][y] = EL_QUICKSAND_EMPTY;
8087 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8088 Store[x][y + 1] = Store[x][y];
8091 PlayLevelSoundAction(x, y, ACTION_FILLING);
8094 else if (element == EL_QUICKSAND_FAST_FULL)
8096 if (IS_FREE(x, y + 1))
8098 InitMovingField(x, y, MV_DOWN);
8099 started_moving = TRUE;
8101 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
8102 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8103 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8104 Store[x][y] = EL_ROCK;
8106 Store[x][y] = EL_ROCK;
8109 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8111 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8113 if (!MovDelay[x][y])
8115 MovDelay[x][y] = TILEY + 1;
8117 ResetGfxAnimation(x, y);
8118 ResetGfxAnimation(x, y + 1);
8123 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8124 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8131 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8132 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8133 Store[x][y + 1] = Store[x][y];
8136 PlayLevelSoundAction(x, y, ACTION_FILLING);
8138 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8140 if (!MovDelay[x][y])
8142 MovDelay[x][y] = TILEY + 1;
8144 ResetGfxAnimation(x, y);
8145 ResetGfxAnimation(x, y + 1);
8150 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8151 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8158 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8159 Feld[x][y + 1] = EL_QUICKSAND_FULL;
8160 Store[x][y + 1] = Store[x][y];
8163 PlayLevelSoundAction(x, y, ACTION_FILLING);
8166 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8167 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8169 InitMovingField(x, y, MV_DOWN);
8170 started_moving = TRUE;
8172 Feld[x][y] = EL_QUICKSAND_FILLING;
8173 Store[x][y] = element;
8175 PlayLevelSoundAction(x, y, ACTION_FILLING);
8177 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8178 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8180 InitMovingField(x, y, MV_DOWN);
8181 started_moving = TRUE;
8183 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
8184 Store[x][y] = element;
8186 PlayLevelSoundAction(x, y, ACTION_FILLING);
8188 else if (element == EL_MAGIC_WALL_FULL)
8190 if (IS_FREE(x, y + 1))
8192 InitMovingField(x, y, MV_DOWN);
8193 started_moving = TRUE;
8195 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
8196 Store[x][y] = EL_CHANGED(Store[x][y]);
8198 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8200 if (!MovDelay[x][y])
8201 MovDelay[x][y] = TILEY / 4 + 1;
8210 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
8211 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
8212 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8216 else if (element == EL_BD_MAGIC_WALL_FULL)
8218 if (IS_FREE(x, y + 1))
8220 InitMovingField(x, y, MV_DOWN);
8221 started_moving = TRUE;
8223 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8224 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8226 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8228 if (!MovDelay[x][y])
8229 MovDelay[x][y] = TILEY / 4 + 1;
8238 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8239 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8240 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8244 else if (element == EL_DC_MAGIC_WALL_FULL)
8246 if (IS_FREE(x, y + 1))
8248 InitMovingField(x, y, MV_DOWN);
8249 started_moving = TRUE;
8251 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8252 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8254 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8256 if (!MovDelay[x][y])
8257 MovDelay[x][y] = TILEY / 4 + 1;
8266 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8267 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8268 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8272 else if ((CAN_PASS_MAGIC_WALL(element) &&
8273 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8274 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8275 (CAN_PASS_DC_MAGIC_WALL(element) &&
8276 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8279 InitMovingField(x, y, MV_DOWN);
8280 started_moving = TRUE;
8283 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8284 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8285 EL_DC_MAGIC_WALL_FILLING);
8286 Store[x][y] = element;
8288 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8290 SplashAcid(x, y + 1);
8292 InitMovingField(x, y, MV_DOWN);
8293 started_moving = TRUE;
8295 Store[x][y] = EL_ACID;
8298 #if USE_FIX_IMPACT_COLLISION
8299 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8300 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8302 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8303 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8305 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8306 CAN_FALL(element) && WasJustFalling[x][y] &&
8307 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8309 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8310 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8311 (Feld[x][y + 1] == EL_BLOCKED)))
8313 /* this is needed for a special case not covered by calling "Impact()"
8314 from "ContinueMoving()": if an element moves to a tile directly below
8315 another element which was just falling on that tile (which was empty
8316 in the previous frame), the falling element above would just stop
8317 instead of smashing the element below (in previous version, the above
8318 element was just checked for "moving" instead of "falling", resulting
8319 in incorrect smashes caused by horizontal movement of the above
8320 element; also, the case of the player being the element to smash was
8321 simply not covered here... :-/ ) */
8323 CheckCollision[x][y] = 0;
8324 CheckImpact[x][y] = 0;
8328 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8330 if (MovDir[x][y] == MV_NONE)
8332 InitMovingField(x, y, MV_DOWN);
8333 started_moving = TRUE;
8336 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8338 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8339 MovDir[x][y] = MV_DOWN;
8341 InitMovingField(x, y, MV_DOWN);
8342 started_moving = TRUE;
8344 else if (element == EL_AMOEBA_DROP)
8346 Feld[x][y] = EL_AMOEBA_GROWING;
8347 Store[x][y] = EL_AMOEBA_WET;
8349 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8350 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8351 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8352 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8354 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
8355 (IS_FREE(x - 1, y + 1) ||
8356 Feld[x - 1][y + 1] == EL_ACID));
8357 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8358 (IS_FREE(x + 1, y + 1) ||
8359 Feld[x + 1][y + 1] == EL_ACID));
8360 boolean can_fall_any = (can_fall_left || can_fall_right);
8361 boolean can_fall_both = (can_fall_left && can_fall_right);
8362 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8364 #if USE_NEW_ALL_SLIPPERY
8365 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8367 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8368 can_fall_right = FALSE;
8369 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8370 can_fall_left = FALSE;
8371 else if (slippery_type == SLIPPERY_ONLY_LEFT)
8372 can_fall_right = FALSE;
8373 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8374 can_fall_left = FALSE;
8376 can_fall_any = (can_fall_left || can_fall_right);
8377 can_fall_both = FALSE;
8380 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8382 if (slippery_type == SLIPPERY_ONLY_LEFT)
8383 can_fall_right = FALSE;
8384 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8385 can_fall_left = FALSE;
8386 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8387 can_fall_right = FALSE;
8388 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8389 can_fall_left = FALSE;
8391 can_fall_any = (can_fall_left || can_fall_right);
8392 can_fall_both = (can_fall_left && can_fall_right);
8396 #if USE_NEW_ALL_SLIPPERY
8398 #if USE_NEW_SP_SLIPPERY
8399 /* !!! better use the same properties as for custom elements here !!! */
8400 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8401 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8403 can_fall_right = FALSE; /* slip down on left side */
8404 can_fall_both = FALSE;
8409 #if USE_NEW_ALL_SLIPPERY
8412 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8413 can_fall_right = FALSE; /* slip down on left side */
8415 can_fall_left = !(can_fall_right = RND(2));
8417 can_fall_both = FALSE;
8422 if (game.emulation == EMU_BOULDERDASH ||
8423 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8424 can_fall_right = FALSE; /* slip down on left side */
8426 can_fall_left = !(can_fall_right = RND(2));
8428 can_fall_both = FALSE;
8434 /* if not determined otherwise, prefer left side for slipping down */
8435 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8436 started_moving = TRUE;
8440 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8442 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8445 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
8446 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8447 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8448 int belt_dir = game.belt_dir[belt_nr];
8450 if ((belt_dir == MV_LEFT && left_is_free) ||
8451 (belt_dir == MV_RIGHT && right_is_free))
8453 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8455 InitMovingField(x, y, belt_dir);
8456 started_moving = TRUE;
8458 Pushed[x][y] = TRUE;
8459 Pushed[nextx][y] = TRUE;
8461 GfxAction[x][y] = ACTION_DEFAULT;
8465 MovDir[x][y] = 0; /* if element was moving, stop it */
8470 /* not "else if" because of elements that can fall and move (EL_SPRING) */
8472 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8474 if (CAN_MOVE(element) && !started_moving)
8477 int move_pattern = element_info[element].move_pattern;
8482 if (MovDir[x][y] == MV_NONE)
8484 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8485 x, y, element, element_info[element].token_name);
8486 printf("StartMoving(): This should never happen!\n");
8491 Moving2Blocked(x, y, &newx, &newy);
8493 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8496 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8497 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8499 WasJustMoving[x][y] = 0;
8500 CheckCollision[x][y] = 0;
8502 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8504 if (Feld[x][y] != element) /* element has changed */
8508 if (!MovDelay[x][y]) /* start new movement phase */
8510 /* all objects that can change their move direction after each step
8511 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8513 if (element != EL_YAMYAM &&
8514 element != EL_DARK_YAMYAM &&
8515 element != EL_PACMAN &&
8516 !(move_pattern & MV_ANY_DIRECTION) &&
8517 move_pattern != MV_TURNING_LEFT &&
8518 move_pattern != MV_TURNING_RIGHT &&
8519 move_pattern != MV_TURNING_LEFT_RIGHT &&
8520 move_pattern != MV_TURNING_RIGHT_LEFT &&
8521 move_pattern != MV_TURNING_RANDOM)
8525 if (MovDelay[x][y] && (element == EL_BUG ||
8526 element == EL_SPACESHIP ||
8527 element == EL_SP_SNIKSNAK ||
8528 element == EL_SP_ELECTRON ||
8529 element == EL_MOLE))
8530 TEST_DrawLevelField(x, y);
8534 if (MovDelay[x][y]) /* wait some time before next movement */
8538 if (element == EL_ROBOT ||
8539 element == EL_YAMYAM ||
8540 element == EL_DARK_YAMYAM)
8542 DrawLevelElementAnimationIfNeeded(x, y, element);
8543 PlayLevelSoundAction(x, y, ACTION_WAITING);
8545 else if (element == EL_SP_ELECTRON)
8546 DrawLevelElementAnimationIfNeeded(x, y, element);
8547 else if (element == EL_DRAGON)
8550 int dir = MovDir[x][y];
8551 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8552 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
8553 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
8554 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
8555 dir == MV_UP ? IMG_FLAMES_1_UP :
8556 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8557 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8559 GfxAction[x][y] = ACTION_ATTACKING;
8561 if (IS_PLAYER(x, y))
8562 DrawPlayerField(x, y);
8564 TEST_DrawLevelField(x, y);
8566 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8568 for (i = 1; i <= 3; i++)
8570 int xx = x + i * dx;
8571 int yy = y + i * dy;
8572 int sx = SCREENX(xx);
8573 int sy = SCREENY(yy);
8574 int flame_graphic = graphic + (i - 1);
8576 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8581 int flamed = MovingOrBlocked2Element(xx, yy);
8585 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8587 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8588 RemoveMovingField(xx, yy);
8590 RemoveField(xx, yy);
8592 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8595 RemoveMovingField(xx, yy);
8598 ChangeDelay[xx][yy] = 0;
8600 Feld[xx][yy] = EL_FLAMES;
8602 if (IN_SCR_FIELD(sx, sy))
8604 TEST_DrawLevelFieldCrumbled(xx, yy);
8605 DrawGraphic(sx, sy, flame_graphic, frame);
8610 if (Feld[xx][yy] == EL_FLAMES)
8611 Feld[xx][yy] = EL_EMPTY;
8612 TEST_DrawLevelField(xx, yy);
8617 if (MovDelay[x][y]) /* element still has to wait some time */
8619 PlayLevelSoundAction(x, y, ACTION_WAITING);
8625 /* now make next step */
8627 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8629 if (DONT_COLLIDE_WITH(element) &&
8630 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8631 !PLAYER_ENEMY_PROTECTED(newx, newy))
8633 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8638 else if (CAN_MOVE_INTO_ACID(element) &&
8639 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8640 !IS_MV_DIAGONAL(MovDir[x][y]) &&
8641 (MovDir[x][y] == MV_DOWN ||
8642 game.engine_version >= VERSION_IDENT(3,1,0,0)))
8644 SplashAcid(newx, newy);
8645 Store[x][y] = EL_ACID;
8647 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8649 if (Feld[newx][newy] == EL_EXIT_OPEN ||
8650 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8651 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8652 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8655 TEST_DrawLevelField(x, y);
8657 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8658 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8659 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8661 local_player->friends_still_needed--;
8662 if (!local_player->friends_still_needed &&
8663 !local_player->GameOver && AllPlayersGone)
8664 PlayerWins(local_player);
8668 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8670 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8671 TEST_DrawLevelField(newx, newy);
8673 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8675 else if (!IS_FREE(newx, newy))
8677 GfxAction[x][y] = ACTION_WAITING;
8679 if (IS_PLAYER(x, y))
8680 DrawPlayerField(x, y);
8682 TEST_DrawLevelField(x, y);
8687 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8689 if (IS_FOOD_PIG(Feld[newx][newy]))
8691 if (IS_MOVING(newx, newy))
8692 RemoveMovingField(newx, newy);
8695 Feld[newx][newy] = EL_EMPTY;
8696 TEST_DrawLevelField(newx, newy);
8699 PlayLevelSound(x, y, SND_PIG_DIGGING);
8701 else if (!IS_FREE(newx, newy))
8703 if (IS_PLAYER(x, y))
8704 DrawPlayerField(x, y);
8706 TEST_DrawLevelField(x, y);
8711 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8713 if (Store[x][y] != EL_EMPTY)
8715 boolean can_clone = FALSE;
8718 /* check if element to clone is still there */
8719 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8721 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8729 /* cannot clone or target field not free anymore -- do not clone */
8730 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8731 Store[x][y] = EL_EMPTY;
8734 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8736 if (IS_MV_DIAGONAL(MovDir[x][y]))
8738 int diagonal_move_dir = MovDir[x][y];
8739 int stored = Store[x][y];
8740 int change_delay = 8;
8743 /* android is moving diagonally */
8745 CreateField(x, y, EL_DIAGONAL_SHRINKING);
8747 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8748 GfxElement[x][y] = EL_EMC_ANDROID;
8749 GfxAction[x][y] = ACTION_SHRINKING;
8750 GfxDir[x][y] = diagonal_move_dir;
8751 ChangeDelay[x][y] = change_delay;
8753 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8756 DrawLevelGraphicAnimation(x, y, graphic);
8757 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8759 if (Feld[newx][newy] == EL_ACID)
8761 SplashAcid(newx, newy);
8766 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8768 Store[newx][newy] = EL_EMC_ANDROID;
8769 GfxElement[newx][newy] = EL_EMC_ANDROID;
8770 GfxAction[newx][newy] = ACTION_GROWING;
8771 GfxDir[newx][newy] = diagonal_move_dir;
8772 ChangeDelay[newx][newy] = change_delay;
8774 graphic = el_act_dir2img(GfxElement[newx][newy],
8775 GfxAction[newx][newy], GfxDir[newx][newy]);
8777 DrawLevelGraphicAnimation(newx, newy, graphic);
8778 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8784 Feld[newx][newy] = EL_EMPTY;
8785 TEST_DrawLevelField(newx, newy);
8787 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8790 else if (!IS_FREE(newx, newy))
8793 if (IS_PLAYER(x, y))
8794 DrawPlayerField(x, y);
8796 TEST_DrawLevelField(x, y);
8802 else if (IS_CUSTOM_ELEMENT(element) &&
8803 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8806 if (!DigFieldByCE(newx, newy, element))
8809 int new_element = Feld[newx][newy];
8811 if (!IS_FREE(newx, newy))
8813 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8814 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8817 /* no element can dig solid indestructible elements */
8818 if (IS_INDESTRUCTIBLE(new_element) &&
8819 !IS_DIGGABLE(new_element) &&
8820 !IS_COLLECTIBLE(new_element))
8823 if (AmoebaNr[newx][newy] &&
8824 (new_element == EL_AMOEBA_FULL ||
8825 new_element == EL_BD_AMOEBA ||
8826 new_element == EL_AMOEBA_GROWING))
8828 AmoebaCnt[AmoebaNr[newx][newy]]--;
8829 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8832 if (IS_MOVING(newx, newy))
8833 RemoveMovingField(newx, newy);
8836 RemoveField(newx, newy);
8837 TEST_DrawLevelField(newx, newy);
8840 /* if digged element was about to explode, prevent the explosion */
8841 ExplodeField[newx][newy] = EX_TYPE_NONE;
8843 PlayLevelSoundAction(x, y, action);
8846 Store[newx][newy] = EL_EMPTY;
8849 /* this makes it possible to leave the removed element again */
8850 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8851 Store[newx][newy] = new_element;
8853 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8855 int move_leave_element = element_info[element].move_leave_element;
8857 /* this makes it possible to leave the removed element again */
8858 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8859 new_element : move_leave_element);
8865 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8867 RunnerVisit[x][y] = FrameCounter;
8868 PlayerVisit[x][y] /= 8; /* expire player visit path */
8871 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8873 if (!IS_FREE(newx, newy))
8875 if (IS_PLAYER(x, y))
8876 DrawPlayerField(x, y);
8878 TEST_DrawLevelField(x, y);
8884 boolean wanna_flame = !RND(10);
8885 int dx = newx - x, dy = newy - y;
8886 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8887 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8888 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8889 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8890 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8891 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8894 IS_CLASSIC_ENEMY(element1) ||
8895 IS_CLASSIC_ENEMY(element2)) &&
8896 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8897 element1 != EL_FLAMES && element2 != EL_FLAMES)
8899 ResetGfxAnimation(x, y);
8900 GfxAction[x][y] = ACTION_ATTACKING;
8902 if (IS_PLAYER(x, y))
8903 DrawPlayerField(x, y);
8905 TEST_DrawLevelField(x, y);
8907 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8909 MovDelay[x][y] = 50;
8913 RemoveField(newx, newy);
8915 Feld[newx][newy] = EL_FLAMES;
8916 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8919 RemoveField(newx1, newy1);
8921 Feld[newx1][newy1] = EL_FLAMES;
8923 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8926 RemoveField(newx2, newy2);
8928 Feld[newx2][newy2] = EL_FLAMES;
8935 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8936 Feld[newx][newy] == EL_DIAMOND)
8938 if (IS_MOVING(newx, newy))
8939 RemoveMovingField(newx, newy);
8942 Feld[newx][newy] = EL_EMPTY;
8943 TEST_DrawLevelField(newx, newy);
8946 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8948 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8949 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8951 if (AmoebaNr[newx][newy])
8953 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8954 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8955 Feld[newx][newy] == EL_BD_AMOEBA)
8956 AmoebaCnt[AmoebaNr[newx][newy]]--;
8961 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8963 RemoveMovingField(newx, newy);
8966 if (IS_MOVING(newx, newy))
8968 RemoveMovingField(newx, newy);
8973 Feld[newx][newy] = EL_EMPTY;
8974 TEST_DrawLevelField(newx, newy);
8977 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8979 else if ((element == EL_PACMAN || element == EL_MOLE)
8980 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8982 if (AmoebaNr[newx][newy])
8984 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8985 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8986 Feld[newx][newy] == EL_BD_AMOEBA)
8987 AmoebaCnt[AmoebaNr[newx][newy]]--;
8990 if (element == EL_MOLE)
8992 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8993 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8995 ResetGfxAnimation(x, y);
8996 GfxAction[x][y] = ACTION_DIGGING;
8997 TEST_DrawLevelField(x, y);
8999 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
9001 return; /* wait for shrinking amoeba */
9003 else /* element == EL_PACMAN */
9005 Feld[newx][newy] = EL_EMPTY;
9006 TEST_DrawLevelField(newx, newy);
9007 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
9010 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
9011 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
9012 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
9014 /* wait for shrinking amoeba to completely disappear */
9017 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
9019 /* object was running against a wall */
9024 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
9025 if (move_pattern & MV_ANY_DIRECTION &&
9026 move_pattern == MovDir[x][y])
9028 int blocking_element =
9029 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
9031 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
9034 element = Feld[x][y]; /* element might have changed */
9038 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
9039 DrawLevelElementAnimation(x, y, element);
9041 if (DONT_TOUCH(element))
9042 TestIfBadThingTouchesPlayer(x, y);
9047 InitMovingField(x, y, MovDir[x][y]);
9049 PlayLevelSoundAction(x, y, ACTION_MOVING);
9053 ContinueMoving(x, y);
9056 void ContinueMoving(int x, int y)
9058 int element = Feld[x][y];
9059 struct ElementInfo *ei = &element_info[element];
9060 int direction = MovDir[x][y];
9061 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9062 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9063 int newx = x + dx, newy = y + dy;
9064 int stored = Store[x][y];
9065 int stored_new = Store[newx][newy];
9066 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
9067 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
9068 boolean last_line = (newy == lev_fieldy - 1);
9070 MovPos[x][y] += getElementMoveStepsize(x, y);
9072 if (pushed_by_player) /* special case: moving object pushed by player */
9073 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
9075 if (ABS(MovPos[x][y]) < TILEX)
9078 int ee = Feld[x][y];
9079 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9080 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
9082 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
9083 x, y, ABS(MovPos[x][y]),
9085 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
9088 TEST_DrawLevelField(x, y);
9090 return; /* element is still moving */
9093 /* element reached destination field */
9095 Feld[x][y] = EL_EMPTY;
9096 Feld[newx][newy] = element;
9097 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
9099 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
9101 element = Feld[newx][newy] = EL_ACID;
9103 else if (element == EL_MOLE)
9105 Feld[x][y] = EL_SAND;
9107 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9109 else if (element == EL_QUICKSAND_FILLING)
9111 element = Feld[newx][newy] = get_next_element(element);
9112 Store[newx][newy] = Store[x][y];
9114 else if (element == EL_QUICKSAND_EMPTYING)
9116 Feld[x][y] = get_next_element(element);
9117 element = Feld[newx][newy] = Store[x][y];
9119 else if (element == EL_QUICKSAND_FAST_FILLING)
9121 element = Feld[newx][newy] = get_next_element(element);
9122 Store[newx][newy] = Store[x][y];
9124 else if (element == EL_QUICKSAND_FAST_EMPTYING)
9126 Feld[x][y] = get_next_element(element);
9127 element = Feld[newx][newy] = Store[x][y];
9129 else if (element == EL_MAGIC_WALL_FILLING)
9131 element = Feld[newx][newy] = get_next_element(element);
9132 if (!game.magic_wall_active)
9133 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
9134 Store[newx][newy] = Store[x][y];
9136 else if (element == EL_MAGIC_WALL_EMPTYING)
9138 Feld[x][y] = get_next_element(element);
9139 if (!game.magic_wall_active)
9140 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9141 element = Feld[newx][newy] = Store[x][y];
9143 #if USE_NEW_CUSTOM_VALUE
9144 InitField(newx, newy, FALSE);
9147 else if (element == EL_BD_MAGIC_WALL_FILLING)
9149 element = Feld[newx][newy] = get_next_element(element);
9150 if (!game.magic_wall_active)
9151 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
9152 Store[newx][newy] = Store[x][y];
9154 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
9156 Feld[x][y] = get_next_element(element);
9157 if (!game.magic_wall_active)
9158 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9159 element = Feld[newx][newy] = Store[x][y];
9161 #if USE_NEW_CUSTOM_VALUE
9162 InitField(newx, newy, FALSE);
9165 else if (element == EL_DC_MAGIC_WALL_FILLING)
9167 element = Feld[newx][newy] = get_next_element(element);
9168 if (!game.magic_wall_active)
9169 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
9170 Store[newx][newy] = Store[x][y];
9172 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
9174 Feld[x][y] = get_next_element(element);
9175 if (!game.magic_wall_active)
9176 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
9177 element = Feld[newx][newy] = Store[x][y];
9179 #if USE_NEW_CUSTOM_VALUE
9180 InitField(newx, newy, FALSE);
9183 else if (element == EL_AMOEBA_DROPPING)
9185 Feld[x][y] = get_next_element(element);
9186 element = Feld[newx][newy] = Store[x][y];
9188 else if (element == EL_SOKOBAN_OBJECT)
9191 Feld[x][y] = Back[x][y];
9193 if (Back[newx][newy])
9194 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
9196 Back[x][y] = Back[newx][newy] = 0;
9199 Store[x][y] = EL_EMPTY;
9204 MovDelay[newx][newy] = 0;
9206 if (CAN_CHANGE_OR_HAS_ACTION(element))
9208 /* copy element change control values to new field */
9209 ChangeDelay[newx][newy] = ChangeDelay[x][y];
9210 ChangePage[newx][newy] = ChangePage[x][y];
9211 ChangeCount[newx][newy] = ChangeCount[x][y];
9212 ChangeEvent[newx][newy] = ChangeEvent[x][y];
9215 #if USE_NEW_CUSTOM_VALUE
9216 CustomValue[newx][newy] = CustomValue[x][y];
9219 ChangeDelay[x][y] = 0;
9220 ChangePage[x][y] = -1;
9221 ChangeCount[x][y] = 0;
9222 ChangeEvent[x][y] = -1;
9224 #if USE_NEW_CUSTOM_VALUE
9225 CustomValue[x][y] = 0;
9228 /* copy animation control values to new field */
9229 GfxFrame[newx][newy] = GfxFrame[x][y];
9230 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
9231 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
9232 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
9234 Pushed[x][y] = Pushed[newx][newy] = FALSE;
9236 /* some elements can leave other elements behind after moving */
9238 if (ei->move_leave_element != EL_EMPTY &&
9239 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9240 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9242 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
9243 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9244 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9247 int move_leave_element = ei->move_leave_element;
9251 /* this makes it possible to leave the removed element again */
9252 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9253 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9255 /* this makes it possible to leave the removed element again */
9256 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9257 move_leave_element = stored;
9260 /* this makes it possible to leave the removed element again */
9261 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
9262 ei->move_leave_element == EL_TRIGGER_ELEMENT)
9263 move_leave_element = stored;
9266 Feld[x][y] = move_leave_element;
9268 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
9269 MovDir[x][y] = direction;
9271 InitField(x, y, FALSE);
9273 if (GFX_CRUMBLED(Feld[x][y]))
9274 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9276 if (ELEM_IS_PLAYER(move_leave_element))
9277 RelocatePlayer(x, y, move_leave_element);
9280 /* do this after checking for left-behind element */
9281 ResetGfxAnimation(x, y); /* reset animation values for old field */
9283 if (!CAN_MOVE(element) ||
9284 (CAN_FALL(element) && direction == MV_DOWN &&
9285 (element == EL_SPRING ||
9286 element_info[element].move_pattern == MV_WHEN_PUSHED ||
9287 element_info[element].move_pattern == MV_WHEN_DROPPED)))
9288 GfxDir[x][y] = MovDir[newx][newy] = 0;
9290 TEST_DrawLevelField(x, y);
9291 TEST_DrawLevelField(newx, newy);
9293 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
9295 /* prevent pushed element from moving on in pushed direction */
9296 if (pushed_by_player && CAN_MOVE(element) &&
9297 element_info[element].move_pattern & MV_ANY_DIRECTION &&
9298 !(element_info[element].move_pattern & direction))
9299 TurnRound(newx, newy);
9301 /* prevent elements on conveyor belt from moving on in last direction */
9302 if (pushed_by_conveyor && CAN_FALL(element) &&
9303 direction & MV_HORIZONTAL)
9304 MovDir[newx][newy] = 0;
9306 if (!pushed_by_player)
9308 int nextx = newx + dx, nexty = newy + dy;
9309 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9311 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9313 if (CAN_FALL(element) && direction == MV_DOWN)
9314 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9316 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9317 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9319 #if USE_FIX_IMPACT_COLLISION
9320 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9321 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9325 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
9327 TestIfBadThingTouchesPlayer(newx, newy);
9328 TestIfBadThingTouchesFriend(newx, newy);
9330 if (!IS_CUSTOM_ELEMENT(element))
9331 TestIfBadThingTouchesOtherBadThing(newx, newy);
9333 else if (element == EL_PENGUIN)
9334 TestIfFriendTouchesBadThing(newx, newy);
9336 if (DONT_GET_HIT_BY(element))
9338 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9341 /* give the player one last chance (one more frame) to move away */
9342 if (CAN_FALL(element) && direction == MV_DOWN &&
9343 (last_line || (!IS_FREE(x, newy + 1) &&
9344 (!IS_PLAYER(x, newy + 1) ||
9345 game.engine_version < VERSION_IDENT(3,1,1,0)))))
9348 if (pushed_by_player && !game.use_change_when_pushing_bug)
9350 int push_side = MV_DIR_OPPOSITE(direction);
9351 struct PlayerInfo *player = PLAYERINFO(x, y);
9353 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9354 player->index_bit, push_side);
9355 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9356 player->index_bit, push_side);
9359 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
9360 MovDelay[newx][newy] = 1;
9362 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9364 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
9367 if (ChangePage[newx][newy] != -1) /* delayed change */
9369 int page = ChangePage[newx][newy];
9370 struct ElementChangeInfo *change = &ei->change_page[page];
9372 ChangePage[newx][newy] = -1;
9374 if (change->can_change)
9376 if (ChangeElement(newx, newy, element, page))
9378 if (change->post_change_function)
9379 change->post_change_function(newx, newy);
9383 if (change->has_action)
9384 ExecuteCustomElementAction(newx, newy, element, page);
9388 TestIfElementHitsCustomElement(newx, newy, direction);
9389 TestIfPlayerTouchesCustomElement(newx, newy);
9390 TestIfElementTouchesCustomElement(newx, newy);
9392 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9393 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9394 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9395 MV_DIR_OPPOSITE(direction));
9398 int AmoebeNachbarNr(int ax, int ay)
9401 int element = Feld[ax][ay];
9403 static int xy[4][2] =
9411 for (i = 0; i < NUM_DIRECTIONS; i++)
9413 int x = ax + xy[i][0];
9414 int y = ay + xy[i][1];
9416 if (!IN_LEV_FIELD(x, y))
9419 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9420 group_nr = AmoebaNr[x][y];
9426 void AmoebenVereinigen(int ax, int ay)
9428 int i, x, y, xx, yy;
9429 int new_group_nr = AmoebaNr[ax][ay];
9430 static int xy[4][2] =
9438 if (new_group_nr == 0)
9441 for (i = 0; i < NUM_DIRECTIONS; i++)
9446 if (!IN_LEV_FIELD(x, y))
9449 if ((Feld[x][y] == EL_AMOEBA_FULL ||
9450 Feld[x][y] == EL_BD_AMOEBA ||
9451 Feld[x][y] == EL_AMOEBA_DEAD) &&
9452 AmoebaNr[x][y] != new_group_nr)
9454 int old_group_nr = AmoebaNr[x][y];
9456 if (old_group_nr == 0)
9459 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9460 AmoebaCnt[old_group_nr] = 0;
9461 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9462 AmoebaCnt2[old_group_nr] = 0;
9464 SCAN_PLAYFIELD(xx, yy)
9466 if (AmoebaNr[xx][yy] == old_group_nr)
9467 AmoebaNr[xx][yy] = new_group_nr;
9473 void AmoebeUmwandeln(int ax, int ay)
9477 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9479 int group_nr = AmoebaNr[ax][ay];
9484 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9485 printf("AmoebeUmwandeln(): This should never happen!\n");
9490 SCAN_PLAYFIELD(x, y)
9492 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9495 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9499 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9500 SND_AMOEBA_TURNING_TO_GEM :
9501 SND_AMOEBA_TURNING_TO_ROCK));
9506 static int xy[4][2] =
9514 for (i = 0; i < NUM_DIRECTIONS; i++)
9519 if (!IN_LEV_FIELD(x, y))
9522 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9524 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9525 SND_AMOEBA_TURNING_TO_GEM :
9526 SND_AMOEBA_TURNING_TO_ROCK));
9533 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9536 int group_nr = AmoebaNr[ax][ay];
9537 boolean done = FALSE;
9542 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9543 printf("AmoebeUmwandelnBD(): This should never happen!\n");
9548 SCAN_PLAYFIELD(x, y)
9550 if (AmoebaNr[x][y] == group_nr &&
9551 (Feld[x][y] == EL_AMOEBA_DEAD ||
9552 Feld[x][y] == EL_BD_AMOEBA ||
9553 Feld[x][y] == EL_AMOEBA_GROWING))
9556 Feld[x][y] = new_element;
9557 InitField(x, y, FALSE);
9558 TEST_DrawLevelField(x, y);
9564 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9565 SND_BD_AMOEBA_TURNING_TO_ROCK :
9566 SND_BD_AMOEBA_TURNING_TO_GEM));
9569 void AmoebeWaechst(int x, int y)
9571 static unsigned int sound_delay = 0;
9572 static unsigned int sound_delay_value = 0;
9574 if (!MovDelay[x][y]) /* start new growing cycle */
9578 if (DelayReached(&sound_delay, sound_delay_value))
9580 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9581 sound_delay_value = 30;
9585 if (MovDelay[x][y]) /* wait some time before growing bigger */
9588 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9590 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9591 6 - MovDelay[x][y]);
9593 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9596 if (!MovDelay[x][y])
9598 Feld[x][y] = Store[x][y];
9600 TEST_DrawLevelField(x, y);
9605 void AmoebaDisappearing(int x, int y)
9607 static unsigned int sound_delay = 0;
9608 static unsigned int sound_delay_value = 0;
9610 if (!MovDelay[x][y]) /* start new shrinking cycle */
9614 if (DelayReached(&sound_delay, sound_delay_value))
9615 sound_delay_value = 30;
9618 if (MovDelay[x][y]) /* wait some time before shrinking */
9621 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9623 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9624 6 - MovDelay[x][y]);
9626 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9629 if (!MovDelay[x][y])
9631 Feld[x][y] = EL_EMPTY;
9632 TEST_DrawLevelField(x, y);
9634 /* don't let mole enter this field in this cycle;
9635 (give priority to objects falling to this field from above) */
9641 void AmoebeAbleger(int ax, int ay)
9644 int element = Feld[ax][ay];
9645 int graphic = el2img(element);
9646 int newax = ax, neway = ay;
9647 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9648 static int xy[4][2] =
9656 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9658 Feld[ax][ay] = EL_AMOEBA_DEAD;
9659 TEST_DrawLevelField(ax, ay);
9663 if (IS_ANIMATED(graphic))
9664 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9666 if (!MovDelay[ax][ay]) /* start making new amoeba field */
9667 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9669 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
9672 if (MovDelay[ax][ay])
9676 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9679 int x = ax + xy[start][0];
9680 int y = ay + xy[start][1];
9682 if (!IN_LEV_FIELD(x, y))
9685 if (IS_FREE(x, y) ||
9686 CAN_GROW_INTO(Feld[x][y]) ||
9687 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9688 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9694 if (newax == ax && neway == ay)
9697 else /* normal or "filled" (BD style) amoeba */
9700 boolean waiting_for_player = FALSE;
9702 for (i = 0; i < NUM_DIRECTIONS; i++)
9704 int j = (start + i) % 4;
9705 int x = ax + xy[j][0];
9706 int y = ay + xy[j][1];
9708 if (!IN_LEV_FIELD(x, y))
9711 if (IS_FREE(x, y) ||
9712 CAN_GROW_INTO(Feld[x][y]) ||
9713 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9714 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9720 else if (IS_PLAYER(x, y))
9721 waiting_for_player = TRUE;
9724 if (newax == ax && neway == ay) /* amoeba cannot grow */
9726 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9728 Feld[ax][ay] = EL_AMOEBA_DEAD;
9729 TEST_DrawLevelField(ax, ay);
9730 AmoebaCnt[AmoebaNr[ax][ay]]--;
9732 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
9734 if (element == EL_AMOEBA_FULL)
9735 AmoebeUmwandeln(ax, ay);
9736 else if (element == EL_BD_AMOEBA)
9737 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9742 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9744 /* amoeba gets larger by growing in some direction */
9746 int new_group_nr = AmoebaNr[ax][ay];
9749 if (new_group_nr == 0)
9751 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9752 printf("AmoebeAbleger(): This should never happen!\n");
9757 AmoebaNr[newax][neway] = new_group_nr;
9758 AmoebaCnt[new_group_nr]++;
9759 AmoebaCnt2[new_group_nr]++;
9761 /* if amoeba touches other amoeba(s) after growing, unify them */
9762 AmoebenVereinigen(newax, neway);
9764 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9766 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9772 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9773 (neway == lev_fieldy - 1 && newax != ax))
9775 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
9776 Store[newax][neway] = element;
9778 else if (neway == ay || element == EL_EMC_DRIPPER)
9780 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
9782 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9786 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
9787 Feld[ax][ay] = EL_AMOEBA_DROPPING;
9788 Store[ax][ay] = EL_AMOEBA_DROP;
9789 ContinueMoving(ax, ay);
9793 TEST_DrawLevelField(newax, neway);
9796 void Life(int ax, int ay)
9800 int element = Feld[ax][ay];
9801 int graphic = el2img(element);
9802 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9804 boolean changed = FALSE;
9806 if (IS_ANIMATED(graphic))
9807 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9812 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
9813 MovDelay[ax][ay] = life_time;
9815 if (MovDelay[ax][ay]) /* wait some time before next cycle */
9818 if (MovDelay[ax][ay])
9822 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9824 int xx = ax+x1, yy = ay+y1;
9827 if (!IN_LEV_FIELD(xx, yy))
9830 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9832 int x = xx+x2, y = yy+y2;
9834 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9837 if (((Feld[x][y] == element ||
9838 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9840 (IS_FREE(x, y) && Stop[x][y]))
9844 if (xx == ax && yy == ay) /* field in the middle */
9846 if (nachbarn < life_parameter[0] ||
9847 nachbarn > life_parameter[1])
9849 Feld[xx][yy] = EL_EMPTY;
9851 TEST_DrawLevelField(xx, yy);
9852 Stop[xx][yy] = TRUE;
9856 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9857 { /* free border field */
9858 if (nachbarn >= life_parameter[2] &&
9859 nachbarn <= life_parameter[3])
9861 Feld[xx][yy] = element;
9862 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9864 TEST_DrawLevelField(xx, yy);
9865 Stop[xx][yy] = TRUE;
9872 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9873 SND_GAME_OF_LIFE_GROWING);
9876 static void InitRobotWheel(int x, int y)
9878 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9881 static void RunRobotWheel(int x, int y)
9883 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9886 static void StopRobotWheel(int x, int y)
9888 if (ZX == x && ZY == y)
9892 game.robot_wheel_active = FALSE;
9896 static void InitTimegateWheel(int x, int y)
9898 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9901 static void RunTimegateWheel(int x, int y)
9903 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9906 static void InitMagicBallDelay(int x, int y)
9909 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9911 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9915 static void ActivateMagicBall(int bx, int by)
9919 if (level.ball_random)
9921 int pos_border = RND(8); /* select one of the eight border elements */
9922 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9923 int xx = pos_content % 3;
9924 int yy = pos_content / 3;
9929 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9930 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9934 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9936 int xx = x - bx + 1;
9937 int yy = y - by + 1;
9939 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9940 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9944 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9947 void CheckExit(int x, int y)
9949 if (local_player->gems_still_needed > 0 ||
9950 local_player->sokobanfields_still_needed > 0 ||
9951 local_player->lights_still_needed > 0)
9953 int element = Feld[x][y];
9954 int graphic = el2img(element);
9956 if (IS_ANIMATED(graphic))
9957 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9962 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9965 Feld[x][y] = EL_EXIT_OPENING;
9967 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9970 void CheckExitEM(int x, int y)
9972 if (local_player->gems_still_needed > 0 ||
9973 local_player->sokobanfields_still_needed > 0 ||
9974 local_player->lights_still_needed > 0)
9976 int element = Feld[x][y];
9977 int graphic = el2img(element);
9979 if (IS_ANIMATED(graphic))
9980 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9985 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9988 Feld[x][y] = EL_EM_EXIT_OPENING;
9990 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9993 void CheckExitSteel(int x, int y)
9995 if (local_player->gems_still_needed > 0 ||
9996 local_player->sokobanfields_still_needed > 0 ||
9997 local_player->lights_still_needed > 0)
9999 int element = Feld[x][y];
10000 int graphic = el2img(element);
10002 if (IS_ANIMATED(graphic))
10003 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10008 if (AllPlayersGone) /* do not re-open exit door closed after last player */
10011 Feld[x][y] = EL_STEEL_EXIT_OPENING;
10013 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
10016 void CheckExitSteelEM(int x, int y)
10018 if (local_player->gems_still_needed > 0 ||
10019 local_player->sokobanfields_still_needed > 0 ||
10020 local_player->lights_still_needed > 0)
10022 int element = Feld[x][y];
10023 int graphic = el2img(element);
10025 if (IS_ANIMATED(graphic))
10026 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10031 if (AllPlayersGone) /* do not re-open exit door closed after last player */
10034 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
10036 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
10039 void CheckExitSP(int x, int y)
10041 if (local_player->gems_still_needed > 0)
10043 int element = Feld[x][y];
10044 int graphic = el2img(element);
10046 if (IS_ANIMATED(graphic))
10047 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10052 if (AllPlayersGone) /* do not re-open exit door closed after last player */
10055 Feld[x][y] = EL_SP_EXIT_OPENING;
10057 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
10060 static void CloseAllOpenTimegates()
10064 SCAN_PLAYFIELD(x, y)
10066 int element = Feld[x][y];
10068 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
10070 Feld[x][y] = EL_TIMEGATE_CLOSING;
10072 PlayLevelSoundAction(x, y, ACTION_CLOSING);
10077 void DrawTwinkleOnField(int x, int y)
10079 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
10082 if (Feld[x][y] == EL_BD_DIAMOND)
10085 if (MovDelay[x][y] == 0) /* next animation frame */
10086 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
10088 if (MovDelay[x][y] != 0) /* wait some time before next frame */
10092 DrawLevelElementAnimation(x, y, Feld[x][y]);
10094 if (MovDelay[x][y] != 0)
10096 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
10097 10 - MovDelay[x][y]);
10099 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
10104 void MauerWaechst(int x, int y)
10108 if (!MovDelay[x][y]) /* next animation frame */
10109 MovDelay[x][y] = 3 * delay;
10111 if (MovDelay[x][y]) /* wait some time before next frame */
10115 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
10117 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
10118 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
10120 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
10123 if (!MovDelay[x][y])
10125 if (MovDir[x][y] == MV_LEFT)
10127 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
10128 TEST_DrawLevelField(x - 1, y);
10130 else if (MovDir[x][y] == MV_RIGHT)
10132 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
10133 TEST_DrawLevelField(x + 1, y);
10135 else if (MovDir[x][y] == MV_UP)
10137 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
10138 TEST_DrawLevelField(x, y - 1);
10142 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
10143 TEST_DrawLevelField(x, y + 1);
10146 Feld[x][y] = Store[x][y];
10148 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
10149 TEST_DrawLevelField(x, y);
10154 void MauerAbleger(int ax, int ay)
10156 int element = Feld[ax][ay];
10157 int graphic = el2img(element);
10158 boolean oben_frei = FALSE, unten_frei = FALSE;
10159 boolean links_frei = FALSE, rechts_frei = FALSE;
10160 boolean oben_massiv = FALSE, unten_massiv = FALSE;
10161 boolean links_massiv = FALSE, rechts_massiv = FALSE;
10162 boolean new_wall = FALSE;
10164 if (IS_ANIMATED(graphic))
10165 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10167 if (!MovDelay[ax][ay]) /* start building new wall */
10168 MovDelay[ax][ay] = 6;
10170 if (MovDelay[ax][ay]) /* wait some time before building new wall */
10172 MovDelay[ax][ay]--;
10173 if (MovDelay[ax][ay])
10177 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10179 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10181 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10183 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10184 rechts_frei = TRUE;
10186 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
10187 element == EL_EXPANDABLE_WALL_ANY)
10191 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
10192 Store[ax][ay-1] = element;
10193 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10194 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10195 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10196 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
10201 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
10202 Store[ax][ay+1] = element;
10203 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10204 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10205 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10206 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
10211 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10212 element == EL_EXPANDABLE_WALL_ANY ||
10213 element == EL_EXPANDABLE_WALL ||
10214 element == EL_BD_EXPANDABLE_WALL)
10218 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
10219 Store[ax-1][ay] = element;
10220 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10221 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10222 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10223 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
10229 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
10230 Store[ax+1][ay] = element;
10231 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10232 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10233 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10234 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
10239 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
10240 TEST_DrawLevelField(ax, ay);
10242 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10243 oben_massiv = TRUE;
10244 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10245 unten_massiv = TRUE;
10246 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10247 links_massiv = TRUE;
10248 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10249 rechts_massiv = TRUE;
10251 if (((oben_massiv && unten_massiv) ||
10252 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10253 element == EL_EXPANDABLE_WALL) &&
10254 ((links_massiv && rechts_massiv) ||
10255 element == EL_EXPANDABLE_WALL_VERTICAL))
10256 Feld[ax][ay] = EL_WALL;
10259 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10262 void MauerAblegerStahl(int ax, int ay)
10264 int element = Feld[ax][ay];
10265 int graphic = el2img(element);
10266 boolean oben_frei = FALSE, unten_frei = FALSE;
10267 boolean links_frei = FALSE, rechts_frei = FALSE;
10268 boolean oben_massiv = FALSE, unten_massiv = FALSE;
10269 boolean links_massiv = FALSE, rechts_massiv = FALSE;
10270 boolean new_wall = FALSE;
10272 if (IS_ANIMATED(graphic))
10273 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10275 if (!MovDelay[ax][ay]) /* start building new wall */
10276 MovDelay[ax][ay] = 6;
10278 if (MovDelay[ax][ay]) /* wait some time before building new wall */
10280 MovDelay[ax][ay]--;
10281 if (MovDelay[ax][ay])
10285 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10287 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10289 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10291 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10292 rechts_frei = TRUE;
10294 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10295 element == EL_EXPANDABLE_STEELWALL_ANY)
10299 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10300 Store[ax][ay-1] = element;
10301 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10302 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10303 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10304 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10309 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10310 Store[ax][ay+1] = element;
10311 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10312 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10313 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10314 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10319 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10320 element == EL_EXPANDABLE_STEELWALL_ANY)
10324 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10325 Store[ax-1][ay] = element;
10326 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10327 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10328 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10329 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10335 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10336 Store[ax+1][ay] = element;
10337 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10338 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10339 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10340 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10345 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10346 oben_massiv = TRUE;
10347 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10348 unten_massiv = TRUE;
10349 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10350 links_massiv = TRUE;
10351 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10352 rechts_massiv = TRUE;
10354 if (((oben_massiv && unten_massiv) ||
10355 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10356 ((links_massiv && rechts_massiv) ||
10357 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10358 Feld[ax][ay] = EL_STEELWALL;
10361 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10364 void CheckForDragon(int x, int y)
10367 boolean dragon_found = FALSE;
10368 static int xy[4][2] =
10376 for (i = 0; i < NUM_DIRECTIONS; i++)
10378 for (j = 0; j < 4; j++)
10380 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10382 if (IN_LEV_FIELD(xx, yy) &&
10383 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10385 if (Feld[xx][yy] == EL_DRAGON)
10386 dragon_found = TRUE;
10395 for (i = 0; i < NUM_DIRECTIONS; i++)
10397 for (j = 0; j < 3; j++)
10399 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10401 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10403 Feld[xx][yy] = EL_EMPTY;
10404 TEST_DrawLevelField(xx, yy);
10413 static void InitBuggyBase(int x, int y)
10415 int element = Feld[x][y];
10416 int activating_delay = FRAMES_PER_SECOND / 4;
10418 ChangeDelay[x][y] =
10419 (element == EL_SP_BUGGY_BASE ?
10420 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10421 element == EL_SP_BUGGY_BASE_ACTIVATING ?
10423 element == EL_SP_BUGGY_BASE_ACTIVE ?
10424 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10427 static void WarnBuggyBase(int x, int y)
10430 static int xy[4][2] =
10438 for (i = 0; i < NUM_DIRECTIONS; i++)
10440 int xx = x + xy[i][0];
10441 int yy = y + xy[i][1];
10443 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10445 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10452 static void InitTrap(int x, int y)
10454 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10457 static void ActivateTrap(int x, int y)
10459 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10462 static void ChangeActiveTrap(int x, int y)
10464 int graphic = IMG_TRAP_ACTIVE;
10466 /* if new animation frame was drawn, correct crumbled sand border */
10467 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10468 TEST_DrawLevelFieldCrumbled(x, y);
10471 static int getSpecialActionElement(int element, int number, int base_element)
10473 return (element != EL_EMPTY ? element :
10474 number != -1 ? base_element + number - 1 :
10478 static int getModifiedActionNumber(int value_old, int operator, int operand,
10479 int value_min, int value_max)
10481 int value_new = (operator == CA_MODE_SET ? operand :
10482 operator == CA_MODE_ADD ? value_old + operand :
10483 operator == CA_MODE_SUBTRACT ? value_old - operand :
10484 operator == CA_MODE_MULTIPLY ? value_old * operand :
10485 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
10486 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
10489 return (value_new < value_min ? value_min :
10490 value_new > value_max ? value_max :
10494 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10496 struct ElementInfo *ei = &element_info[element];
10497 struct ElementChangeInfo *change = &ei->change_page[page];
10498 int target_element = change->target_element;
10499 int action_type = change->action_type;
10500 int action_mode = change->action_mode;
10501 int action_arg = change->action_arg;
10502 int action_element = change->action_element;
10505 if (!change->has_action)
10508 /* ---------- determine action paramater values -------------------------- */
10510 int level_time_value =
10511 (level.time > 0 ? TimeLeft :
10514 int action_arg_element_raw =
10515 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
10516 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10517 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
10518 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
10519 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10520 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
10521 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
10523 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10526 if (action_arg_element_raw == EL_GROUP_START)
10527 printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10530 int action_arg_direction =
10531 (action_arg >= CA_ARG_DIRECTION_LEFT &&
10532 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10533 action_arg == CA_ARG_DIRECTION_TRIGGER ?
10534 change->actual_trigger_side :
10535 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10536 MV_DIR_OPPOSITE(change->actual_trigger_side) :
10539 int action_arg_number_min =
10540 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10543 int action_arg_number_max =
10544 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10545 action_type == CA_SET_LEVEL_GEMS ? 999 :
10546 action_type == CA_SET_LEVEL_TIME ? 9999 :
10547 action_type == CA_SET_LEVEL_SCORE ? 99999 :
10548 action_type == CA_SET_CE_VALUE ? 9999 :
10549 action_type == CA_SET_CE_SCORE ? 9999 :
10552 int action_arg_number_reset =
10553 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10554 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10555 action_type == CA_SET_LEVEL_TIME ? level.time :
10556 action_type == CA_SET_LEVEL_SCORE ? 0 :
10557 #if USE_NEW_CUSTOM_VALUE
10558 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10560 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10562 action_type == CA_SET_CE_SCORE ? 0 :
10565 int action_arg_number =
10566 (action_arg <= CA_ARG_MAX ? action_arg :
10567 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10568 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10569 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10570 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10571 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10572 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10573 #if USE_NEW_CUSTOM_VALUE
10574 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10576 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10578 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10579 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10580 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10581 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10582 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10583 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10584 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10585 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10586 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10587 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10588 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10589 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
10590 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10591 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
10594 int action_arg_number_old =
10595 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10596 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10597 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10598 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10599 action_type == CA_SET_CE_SCORE ? ei->collect_score :
10602 int action_arg_number_new =
10603 getModifiedActionNumber(action_arg_number_old,
10604 action_mode, action_arg_number,
10605 action_arg_number_min, action_arg_number_max);
10608 int trigger_player_bits =
10609 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10610 change->actual_trigger_player_bits : change->trigger_player);
10612 int trigger_player_bits =
10613 (change->actual_trigger_player >= EL_PLAYER_1 &&
10614 change->actual_trigger_player <= EL_PLAYER_4 ?
10615 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10619 int action_arg_player_bits =
10620 (action_arg >= CA_ARG_PLAYER_1 &&
10621 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10622 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10623 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10626 /* ---------- execute action -------------------------------------------- */
10628 switch (action_type)
10635 /* ---------- level actions ------------------------------------------- */
10637 case CA_RESTART_LEVEL:
10639 game.restart_level = TRUE;
10644 case CA_SHOW_ENVELOPE:
10646 int element = getSpecialActionElement(action_arg_element,
10647 action_arg_number, EL_ENVELOPE_1);
10649 if (IS_ENVELOPE(element))
10650 local_player->show_envelope = element;
10655 case CA_SET_LEVEL_TIME:
10657 if (level.time > 0) /* only modify limited time value */
10659 TimeLeft = action_arg_number_new;
10662 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10664 DisplayGameControlValues();
10666 DrawGameValue_Time(TimeLeft);
10669 if (!TimeLeft && setup.time_limit)
10670 for (i = 0; i < MAX_PLAYERS; i++)
10671 KillPlayer(&stored_player[i]);
10677 case CA_SET_LEVEL_SCORE:
10679 local_player->score = action_arg_number_new;
10682 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10684 DisplayGameControlValues();
10686 DrawGameValue_Score(local_player->score);
10692 case CA_SET_LEVEL_GEMS:
10694 local_player->gems_still_needed = action_arg_number_new;
10697 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10699 DisplayGameControlValues();
10701 DrawGameValue_Emeralds(local_player->gems_still_needed);
10707 #if !USE_PLAYER_GRAVITY
10708 case CA_SET_LEVEL_GRAVITY:
10710 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10711 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10712 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10718 case CA_SET_LEVEL_WIND:
10720 game.wind_direction = action_arg_direction;
10725 case CA_SET_LEVEL_RANDOM_SEED:
10728 /* ensure that setting a new random seed while playing is predictable */
10729 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10731 InitRND(action_arg_number_new);
10735 printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10743 for (i = 0; i < 9; i++)
10744 printf("%d, ", RND(2));
10752 /* ---------- player actions ------------------------------------------ */
10754 case CA_MOVE_PLAYER:
10756 /* automatically move to the next field in specified direction */
10757 for (i = 0; i < MAX_PLAYERS; i++)
10758 if (trigger_player_bits & (1 << i))
10759 stored_player[i].programmed_action = action_arg_direction;
10764 case CA_EXIT_PLAYER:
10766 for (i = 0; i < MAX_PLAYERS; i++)
10767 if (action_arg_player_bits & (1 << i))
10768 PlayerWins(&stored_player[i]);
10773 case CA_KILL_PLAYER:
10775 for (i = 0; i < MAX_PLAYERS; i++)
10776 if (action_arg_player_bits & (1 << i))
10777 KillPlayer(&stored_player[i]);
10782 case CA_SET_PLAYER_KEYS:
10784 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10785 int element = getSpecialActionElement(action_arg_element,
10786 action_arg_number, EL_KEY_1);
10788 if (IS_KEY(element))
10790 for (i = 0; i < MAX_PLAYERS; i++)
10792 if (trigger_player_bits & (1 << i))
10794 stored_player[i].key[KEY_NR(element)] = key_state;
10796 DrawGameDoorValues();
10804 case CA_SET_PLAYER_SPEED:
10807 printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10810 for (i = 0; i < MAX_PLAYERS; i++)
10812 if (trigger_player_bits & (1 << i))
10814 int move_stepsize = TILEX / stored_player[i].move_delay_value;
10816 if (action_arg == CA_ARG_SPEED_FASTER &&
10817 stored_player[i].cannot_move)
10819 action_arg_number = STEPSIZE_VERY_SLOW;
10821 else if (action_arg == CA_ARG_SPEED_SLOWER ||
10822 action_arg == CA_ARG_SPEED_FASTER)
10824 action_arg_number = 2;
10825 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10828 else if (action_arg == CA_ARG_NUMBER_RESET)
10830 action_arg_number = level.initial_player_stepsize[i];
10834 getModifiedActionNumber(move_stepsize,
10837 action_arg_number_min,
10838 action_arg_number_max);
10840 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10847 case CA_SET_PLAYER_SHIELD:
10849 for (i = 0; i < MAX_PLAYERS; i++)
10851 if (trigger_player_bits & (1 << i))
10853 if (action_arg == CA_ARG_SHIELD_OFF)
10855 stored_player[i].shield_normal_time_left = 0;
10856 stored_player[i].shield_deadly_time_left = 0;
10858 else if (action_arg == CA_ARG_SHIELD_NORMAL)
10860 stored_player[i].shield_normal_time_left = 999999;
10862 else if (action_arg == CA_ARG_SHIELD_DEADLY)
10864 stored_player[i].shield_normal_time_left = 999999;
10865 stored_player[i].shield_deadly_time_left = 999999;
10873 #if USE_PLAYER_GRAVITY
10874 case CA_SET_PLAYER_GRAVITY:
10876 for (i = 0; i < MAX_PLAYERS; i++)
10878 if (trigger_player_bits & (1 << i))
10880 stored_player[i].gravity =
10881 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10882 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10883 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10884 stored_player[i].gravity);
10892 case CA_SET_PLAYER_ARTWORK:
10894 for (i = 0; i < MAX_PLAYERS; i++)
10896 if (trigger_player_bits & (1 << i))
10898 int artwork_element = action_arg_element;
10900 if (action_arg == CA_ARG_ELEMENT_RESET)
10902 (level.use_artwork_element[i] ? level.artwork_element[i] :
10903 stored_player[i].element_nr);
10905 #if USE_GFX_RESET_PLAYER_ARTWORK
10906 if (stored_player[i].artwork_element != artwork_element)
10907 stored_player[i].Frame = 0;
10910 stored_player[i].artwork_element = artwork_element;
10912 SetPlayerWaiting(&stored_player[i], FALSE);
10914 /* set number of special actions for bored and sleeping animation */
10915 stored_player[i].num_special_action_bored =
10916 get_num_special_action(artwork_element,
10917 ACTION_BORING_1, ACTION_BORING_LAST);
10918 stored_player[i].num_special_action_sleeping =
10919 get_num_special_action(artwork_element,
10920 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10927 case CA_SET_PLAYER_INVENTORY:
10929 for (i = 0; i < MAX_PLAYERS; i++)
10931 struct PlayerInfo *player = &stored_player[i];
10934 if (trigger_player_bits & (1 << i))
10936 int inventory_element = action_arg_element;
10938 if (action_arg == CA_ARG_ELEMENT_TARGET ||
10939 action_arg == CA_ARG_ELEMENT_TRIGGER ||
10940 action_arg == CA_ARG_ELEMENT_ACTION)
10942 int element = inventory_element;
10943 int collect_count = element_info[element].collect_count_initial;
10945 if (!IS_CUSTOM_ELEMENT(element))
10948 if (collect_count == 0)
10949 player->inventory_infinite_element = element;
10951 for (k = 0; k < collect_count; k++)
10952 if (player->inventory_size < MAX_INVENTORY_SIZE)
10953 player->inventory_element[player->inventory_size++] =
10956 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10957 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10958 action_arg == CA_ARG_INVENTORY_RM_ACTION)
10960 if (player->inventory_infinite_element != EL_UNDEFINED &&
10961 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10962 action_arg_element_raw))
10963 player->inventory_infinite_element = EL_UNDEFINED;
10965 for (k = 0, j = 0; j < player->inventory_size; j++)
10967 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10968 action_arg_element_raw))
10969 player->inventory_element[k++] = player->inventory_element[j];
10972 player->inventory_size = k;
10974 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10976 if (player->inventory_size > 0)
10978 for (j = 0; j < player->inventory_size - 1; j++)
10979 player->inventory_element[j] = player->inventory_element[j + 1];
10981 player->inventory_size--;
10984 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10986 if (player->inventory_size > 0)
10987 player->inventory_size--;
10989 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10991 player->inventory_infinite_element = EL_UNDEFINED;
10992 player->inventory_size = 0;
10994 else if (action_arg == CA_ARG_INVENTORY_RESET)
10996 player->inventory_infinite_element = EL_UNDEFINED;
10997 player->inventory_size = 0;
10999 if (level.use_initial_inventory[i])
11001 for (j = 0; j < level.initial_inventory_size[i]; j++)
11003 int element = level.initial_inventory_content[i][j];
11004 int collect_count = element_info[element].collect_count_initial;
11006 if (!IS_CUSTOM_ELEMENT(element))
11009 if (collect_count == 0)
11010 player->inventory_infinite_element = element;
11012 for (k = 0; k < collect_count; k++)
11013 if (player->inventory_size < MAX_INVENTORY_SIZE)
11014 player->inventory_element[player->inventory_size++] =
11025 /* ---------- CE actions ---------------------------------------------- */
11027 case CA_SET_CE_VALUE:
11029 #if USE_NEW_CUSTOM_VALUE
11030 int last_ce_value = CustomValue[x][y];
11032 CustomValue[x][y] = action_arg_number_new;
11034 if (CustomValue[x][y] != last_ce_value)
11036 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
11037 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
11039 if (CustomValue[x][y] == 0)
11041 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
11042 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
11050 case CA_SET_CE_SCORE:
11052 #if USE_NEW_CUSTOM_VALUE
11053 int last_ce_score = ei->collect_score;
11055 ei->collect_score = action_arg_number_new;
11057 if (ei->collect_score != last_ce_score)
11059 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
11060 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
11062 if (ei->collect_score == 0)
11066 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
11067 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
11070 This is a very special case that seems to be a mixture between
11071 CheckElementChange() and CheckTriggeredElementChange(): while
11072 the first one only affects single elements that are triggered
11073 directly, the second one affects multiple elements in the playfield
11074 that are triggered indirectly by another element. This is a third
11075 case: Changing the CE score always affects multiple identical CEs,
11076 so every affected CE must be checked, not only the single CE for
11077 which the CE score was changed in the first place (as every instance
11078 of that CE shares the same CE score, and therefore also can change)!
11080 SCAN_PLAYFIELD(xx, yy)
11082 if (Feld[xx][yy] == element)
11083 CheckElementChange(xx, yy, element, EL_UNDEFINED,
11084 CE_SCORE_GETS_ZERO);
11093 case CA_SET_CE_ARTWORK:
11095 int artwork_element = action_arg_element;
11096 boolean reset_frame = FALSE;
11099 if (action_arg == CA_ARG_ELEMENT_RESET)
11100 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
11103 if (ei->gfx_element != artwork_element)
11104 reset_frame = TRUE;
11106 ei->gfx_element = artwork_element;
11108 SCAN_PLAYFIELD(xx, yy)
11110 if (Feld[xx][yy] == element)
11114 ResetGfxAnimation(xx, yy);
11115 ResetRandomAnimationValue(xx, yy);
11118 TEST_DrawLevelField(xx, yy);
11125 /* ---------- engine actions ------------------------------------------ */
11127 case CA_SET_ENGINE_SCAN_MODE:
11129 InitPlayfieldScanMode(action_arg);
11139 static void CreateFieldExt(int x, int y, int element, boolean is_change)
11141 int old_element = Feld[x][y];
11142 int new_element = GetElementFromGroupElement(element);
11143 int previous_move_direction = MovDir[x][y];
11144 #if USE_NEW_CUSTOM_VALUE
11145 int last_ce_value = CustomValue[x][y];
11147 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
11148 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
11149 boolean add_player_onto_element = (new_element_is_player &&
11150 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
11151 /* this breaks SnakeBite when a snake is
11152 halfway through a door that closes */
11153 /* NOW FIXED AT LEVEL INIT IN files.c */
11154 new_element != EL_SOKOBAN_FIELD_PLAYER &&
11156 IS_WALKABLE(old_element));
11159 /* check if element under the player changes from accessible to unaccessible
11160 (needed for special case of dropping element which then changes) */
11161 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11162 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11170 if (!add_player_onto_element)
11172 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
11173 RemoveMovingField(x, y);
11177 Feld[x][y] = new_element;
11179 #if !USE_GFX_RESET_GFX_ANIMATION
11180 ResetGfxAnimation(x, y);
11181 ResetRandomAnimationValue(x, y);
11184 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
11185 MovDir[x][y] = previous_move_direction;
11187 #if USE_NEW_CUSTOM_VALUE
11188 if (element_info[new_element].use_last_ce_value)
11189 CustomValue[x][y] = last_ce_value;
11192 InitField_WithBug1(x, y, FALSE);
11194 new_element = Feld[x][y]; /* element may have changed */
11196 #if USE_GFX_RESET_GFX_ANIMATION
11197 ResetGfxAnimation(x, y);
11198 ResetRandomAnimationValue(x, y);
11201 TEST_DrawLevelField(x, y);
11203 if (GFX_CRUMBLED(new_element))
11204 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
11208 /* check if element under the player changes from accessible to unaccessible
11209 (needed for special case of dropping element which then changes) */
11210 /* (must be checked after creating new element for walkable group elements) */
11211 #if USE_FIX_KILLED_BY_NON_WALKABLE
11212 if (IS_PLAYER(x, y) && !player_explosion_protected &&
11213 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11220 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11221 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11230 /* "ChangeCount" not set yet to allow "entered by player" change one time */
11231 if (new_element_is_player)
11232 RelocatePlayer(x, y, new_element);
11235 ChangeCount[x][y]++; /* count number of changes in the same frame */
11237 TestIfBadThingTouchesPlayer(x, y);
11238 TestIfPlayerTouchesCustomElement(x, y);
11239 TestIfElementTouchesCustomElement(x, y);
11242 static void CreateField(int x, int y, int element)
11244 CreateFieldExt(x, y, element, FALSE);
11247 static void CreateElementFromChange(int x, int y, int element)
11249 element = GET_VALID_RUNTIME_ELEMENT(element);
11251 #if USE_STOP_CHANGED_ELEMENTS
11252 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11254 int old_element = Feld[x][y];
11256 /* prevent changed element from moving in same engine frame
11257 unless both old and new element can either fall or move */
11258 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
11259 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
11264 CreateFieldExt(x, y, element, TRUE);
11267 static boolean ChangeElement(int x, int y, int element, int page)
11269 struct ElementInfo *ei = &element_info[element];
11270 struct ElementChangeInfo *change = &ei->change_page[page];
11271 int ce_value = CustomValue[x][y];
11272 int ce_score = ei->collect_score;
11273 int target_element;
11274 int old_element = Feld[x][y];
11276 /* always use default change event to prevent running into a loop */
11277 if (ChangeEvent[x][y] == -1)
11278 ChangeEvent[x][y] = CE_DELAY;
11280 if (ChangeEvent[x][y] == CE_DELAY)
11282 /* reset actual trigger element, trigger player and action element */
11283 change->actual_trigger_element = EL_EMPTY;
11284 change->actual_trigger_player = EL_EMPTY;
11285 change->actual_trigger_player_bits = CH_PLAYER_NONE;
11286 change->actual_trigger_side = CH_SIDE_NONE;
11287 change->actual_trigger_ce_value = 0;
11288 change->actual_trigger_ce_score = 0;
11291 /* do not change elements more than a specified maximum number of changes */
11292 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11295 ChangeCount[x][y]++; /* count number of changes in the same frame */
11297 if (change->explode)
11304 if (change->use_target_content)
11306 boolean complete_replace = TRUE;
11307 boolean can_replace[3][3];
11310 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11313 boolean is_walkable;
11314 boolean is_diggable;
11315 boolean is_collectible;
11316 boolean is_removable;
11317 boolean is_destructible;
11318 int ex = x + xx - 1;
11319 int ey = y + yy - 1;
11320 int content_element = change->target_content.e[xx][yy];
11323 can_replace[xx][yy] = TRUE;
11325 if (ex == x && ey == y) /* do not check changing element itself */
11328 if (content_element == EL_EMPTY_SPACE)
11330 can_replace[xx][yy] = FALSE; /* do not replace border with space */
11335 if (!IN_LEV_FIELD(ex, ey))
11337 can_replace[xx][yy] = FALSE;
11338 complete_replace = FALSE;
11345 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11346 e = MovingOrBlocked2Element(ex, ey);
11348 is_empty = (IS_FREE(ex, ey) ||
11349 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11351 is_walkable = (is_empty || IS_WALKABLE(e));
11352 is_diggable = (is_empty || IS_DIGGABLE(e));
11353 is_collectible = (is_empty || IS_COLLECTIBLE(e));
11354 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11355 is_removable = (is_diggable || is_collectible);
11357 can_replace[xx][yy] =
11358 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
11359 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
11360 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
11361 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
11362 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
11363 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11364 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11366 if (!can_replace[xx][yy])
11367 complete_replace = FALSE;
11370 if (!change->only_if_complete || complete_replace)
11372 boolean something_has_changed = FALSE;
11374 if (change->only_if_complete && change->use_random_replace &&
11375 RND(100) < change->random_percentage)
11378 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11380 int ex = x + xx - 1;
11381 int ey = y + yy - 1;
11382 int content_element;
11384 if (can_replace[xx][yy] && (!change->use_random_replace ||
11385 RND(100) < change->random_percentage))
11387 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11388 RemoveMovingField(ex, ey);
11390 ChangeEvent[ex][ey] = ChangeEvent[x][y];
11392 content_element = change->target_content.e[xx][yy];
11393 target_element = GET_TARGET_ELEMENT(element, content_element, change,
11394 ce_value, ce_score);
11396 CreateElementFromChange(ex, ey, target_element);
11398 something_has_changed = TRUE;
11400 /* for symmetry reasons, freeze newly created border elements */
11401 if (ex != x || ey != y)
11402 Stop[ex][ey] = TRUE; /* no more moving in this frame */
11406 if (something_has_changed)
11408 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11409 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11415 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11416 ce_value, ce_score);
11418 if (element == EL_DIAGONAL_GROWING ||
11419 element == EL_DIAGONAL_SHRINKING)
11421 target_element = Store[x][y];
11423 Store[x][y] = EL_EMPTY;
11426 CreateElementFromChange(x, y, target_element);
11428 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11429 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11432 /* this uses direct change before indirect change */
11433 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11438 #if USE_NEW_DELAYED_ACTION
11440 static void HandleElementChange(int x, int y, int page)
11442 int element = MovingOrBlocked2Element(x, y);
11443 struct ElementInfo *ei = &element_info[element];
11444 struct ElementChangeInfo *change = &ei->change_page[page];
11445 boolean handle_action_before_change = FALSE;
11448 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11449 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11452 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11453 x, y, element, element_info[element].token_name);
11454 printf("HandleElementChange(): This should never happen!\n");
11459 /* this can happen with classic bombs on walkable, changing elements */
11460 if (!CAN_CHANGE_OR_HAS_ACTION(element))
11463 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11464 ChangeDelay[x][y] = 0;
11470 if (ChangeDelay[x][y] == 0) /* initialize element change */
11472 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11474 if (change->can_change)
11477 /* !!! not clear why graphic animation should be reset at all here !!! */
11478 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11479 #if USE_GFX_RESET_WHEN_NOT_MOVING
11480 /* when a custom element is about to change (for example by change delay),
11481 do not reset graphic animation when the custom element is moving */
11482 if (!IS_MOVING(x, y))
11485 ResetGfxAnimation(x, y);
11486 ResetRandomAnimationValue(x, y);
11490 if (change->pre_change_function)
11491 change->pre_change_function(x, y);
11495 ChangeDelay[x][y]--;
11497 if (ChangeDelay[x][y] != 0) /* continue element change */
11499 if (change->can_change)
11501 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11503 if (IS_ANIMATED(graphic))
11504 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11506 if (change->change_function)
11507 change->change_function(x, y);
11510 else /* finish element change */
11512 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11514 page = ChangePage[x][y];
11515 ChangePage[x][y] = -1;
11517 change = &ei->change_page[page];
11520 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11522 ChangeDelay[x][y] = 1; /* try change after next move step */
11523 ChangePage[x][y] = page; /* remember page to use for change */
11529 /* special case: set new level random seed before changing element */
11530 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11531 handle_action_before_change = TRUE;
11533 if (change->has_action && handle_action_before_change)
11534 ExecuteCustomElementAction(x, y, element, page);
11537 if (change->can_change)
11539 if (ChangeElement(x, y, element, page))
11541 if (change->post_change_function)
11542 change->post_change_function(x, y);
11546 if (change->has_action && !handle_action_before_change)
11547 ExecuteCustomElementAction(x, y, element, page);
11553 static void HandleElementChange(int x, int y, int page)
11555 int element = MovingOrBlocked2Element(x, y);
11556 struct ElementInfo *ei = &element_info[element];
11557 struct ElementChangeInfo *change = &ei->change_page[page];
11560 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11563 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11564 x, y, element, element_info[element].token_name);
11565 printf("HandleElementChange(): This should never happen!\n");
11570 /* this can happen with classic bombs on walkable, changing elements */
11571 if (!CAN_CHANGE(element))
11574 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11575 ChangeDelay[x][y] = 0;
11581 if (ChangeDelay[x][y] == 0) /* initialize element change */
11583 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11585 ResetGfxAnimation(x, y);
11586 ResetRandomAnimationValue(x, y);
11588 if (change->pre_change_function)
11589 change->pre_change_function(x, y);
11592 ChangeDelay[x][y]--;
11594 if (ChangeDelay[x][y] != 0) /* continue element change */
11596 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11598 if (IS_ANIMATED(graphic))
11599 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11601 if (change->change_function)
11602 change->change_function(x, y);
11604 else /* finish element change */
11606 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11608 page = ChangePage[x][y];
11609 ChangePage[x][y] = -1;
11611 change = &ei->change_page[page];
11614 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11616 ChangeDelay[x][y] = 1; /* try change after next move step */
11617 ChangePage[x][y] = page; /* remember page to use for change */
11622 if (ChangeElement(x, y, element, page))
11624 if (change->post_change_function)
11625 change->post_change_function(x, y);
11632 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11633 int trigger_element,
11635 int trigger_player,
11639 boolean change_done_any = FALSE;
11640 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11643 if (!(trigger_events[trigger_element][trigger_event]))
11647 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11648 trigger_event, recursion_loop_depth, recursion_loop_detected,
11649 recursion_loop_element, EL_NAME(recursion_loop_element));
11652 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11654 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11656 int element = EL_CUSTOM_START + i;
11657 boolean change_done = FALSE;
11660 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11661 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11664 for (p = 0; p < element_info[element].num_change_pages; p++)
11666 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11668 if (change->can_change_or_has_action &&
11669 change->has_event[trigger_event] &&
11670 change->trigger_side & trigger_side &&
11671 change->trigger_player & trigger_player &&
11672 change->trigger_page & trigger_page_bits &&
11673 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11675 change->actual_trigger_element = trigger_element;
11676 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11677 change->actual_trigger_player_bits = trigger_player;
11678 change->actual_trigger_side = trigger_side;
11679 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11680 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11683 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11684 element, EL_NAME(element), p);
11687 if ((change->can_change && !change_done) || change->has_action)
11691 SCAN_PLAYFIELD(x, y)
11693 if (Feld[x][y] == element)
11695 if (change->can_change && !change_done)
11697 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11698 /* if element already changed in this frame, not only prevent
11699 another element change (checked in ChangeElement()), but
11700 also prevent additional element actions for this element */
11702 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11703 !level.use_action_after_change_bug)
11708 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11709 element, EL_NAME(element), p);
11712 ChangeDelay[x][y] = 1;
11713 ChangeEvent[x][y] = trigger_event;
11715 HandleElementChange(x, y, p);
11717 #if USE_NEW_DELAYED_ACTION
11718 else if (change->has_action)
11720 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11721 /* if element already changed in this frame, not only prevent
11722 another element change (checked in ChangeElement()), but
11723 also prevent additional element actions for this element */
11725 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11726 !level.use_action_after_change_bug)
11732 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11733 element, EL_NAME(element), p);
11736 ExecuteCustomElementAction(x, y, element, p);
11737 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11740 if (change->has_action)
11742 ExecuteCustomElementAction(x, y, element, p);
11743 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11749 if (change->can_change)
11751 change_done = TRUE;
11752 change_done_any = TRUE;
11755 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11756 element, EL_NAME(element), p);
11765 RECURSION_LOOP_DETECTION_END();
11767 return change_done_any;
11770 static boolean CheckElementChangeExt(int x, int y,
11772 int trigger_element,
11774 int trigger_player,
11777 boolean change_done = FALSE;
11780 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11781 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11784 if (Feld[x][y] == EL_BLOCKED)
11786 Blocked2Moving(x, y, &x, &y);
11787 element = Feld[x][y];
11791 /* check if element has already changed */
11792 if (Feld[x][y] != element)
11795 /* check if element has already changed or is about to change after moving */
11796 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11797 Feld[x][y] != element) ||
11799 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11800 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11801 ChangePage[x][y] != -1)))
11806 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11807 trigger_event, recursion_loop_depth, recursion_loop_detected,
11808 recursion_loop_element, EL_NAME(recursion_loop_element));
11811 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11814 printf("::: X: trigger_player_bits == %d\n", trigger_player);
11817 for (p = 0; p < element_info[element].num_change_pages; p++)
11819 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11821 /* check trigger element for all events where the element that is checked
11822 for changing interacts with a directly adjacent element -- this is
11823 different to element changes that affect other elements to change on the
11824 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11825 boolean check_trigger_element =
11826 (trigger_event == CE_TOUCHING_X ||
11827 trigger_event == CE_HITTING_X ||
11828 trigger_event == CE_HIT_BY_X ||
11830 /* this one was forgotten until 3.2.3 */
11831 trigger_event == CE_DIGGING_X);
11834 if (change->can_change_or_has_action &&
11835 change->has_event[trigger_event] &&
11836 change->trigger_side & trigger_side &&
11837 change->trigger_player & trigger_player &&
11838 (!check_trigger_element ||
11839 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11841 change->actual_trigger_element = trigger_element;
11842 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11843 change->actual_trigger_player_bits = trigger_player;
11844 change->actual_trigger_side = trigger_side;
11845 change->actual_trigger_ce_value = CustomValue[x][y];
11846 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11848 /* special case: trigger element not at (x,y) position for some events */
11849 if (check_trigger_element)
11861 { 0, 0 }, { 0, 0 }, { 0, 0 },
11865 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11866 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11868 change->actual_trigger_ce_value = CustomValue[xx][yy];
11869 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11872 if (change->can_change && !change_done)
11874 ChangeDelay[x][y] = 1;
11875 ChangeEvent[x][y] = trigger_event;
11877 HandleElementChange(x, y, p);
11879 change_done = TRUE;
11881 #if USE_NEW_DELAYED_ACTION
11882 else if (change->has_action)
11884 ExecuteCustomElementAction(x, y, element, p);
11885 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11888 if (change->has_action)
11890 ExecuteCustomElementAction(x, y, element, p);
11891 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11897 RECURSION_LOOP_DETECTION_END();
11899 return change_done;
11902 static void PlayPlayerSound(struct PlayerInfo *player)
11904 int jx = player->jx, jy = player->jy;
11905 int sound_element = player->artwork_element;
11906 int last_action = player->last_action_waiting;
11907 int action = player->action_waiting;
11909 if (player->is_waiting)
11911 if (action != last_action)
11912 PlayLevelSoundElementAction(jx, jy, sound_element, action);
11914 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11918 if (action != last_action)
11919 StopSound(element_info[sound_element].sound[last_action]);
11921 if (last_action == ACTION_SLEEPING)
11922 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11926 static void PlayAllPlayersSound()
11930 for (i = 0; i < MAX_PLAYERS; i++)
11931 if (stored_player[i].active)
11932 PlayPlayerSound(&stored_player[i]);
11935 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11937 boolean last_waiting = player->is_waiting;
11938 int move_dir = player->MovDir;
11940 player->dir_waiting = move_dir;
11941 player->last_action_waiting = player->action_waiting;
11945 if (!last_waiting) /* not waiting -> waiting */
11947 player->is_waiting = TRUE;
11949 player->frame_counter_bored =
11951 game.player_boring_delay_fixed +
11952 GetSimpleRandom(game.player_boring_delay_random);
11953 player->frame_counter_sleeping =
11955 game.player_sleeping_delay_fixed +
11956 GetSimpleRandom(game.player_sleeping_delay_random);
11958 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11961 if (game.player_sleeping_delay_fixed +
11962 game.player_sleeping_delay_random > 0 &&
11963 player->anim_delay_counter == 0 &&
11964 player->post_delay_counter == 0 &&
11965 FrameCounter >= player->frame_counter_sleeping)
11966 player->is_sleeping = TRUE;
11967 else if (game.player_boring_delay_fixed +
11968 game.player_boring_delay_random > 0 &&
11969 FrameCounter >= player->frame_counter_bored)
11970 player->is_bored = TRUE;
11972 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11973 player->is_bored ? ACTION_BORING :
11976 if (player->is_sleeping && player->use_murphy)
11978 /* special case for sleeping Murphy when leaning against non-free tile */
11980 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11981 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11982 !IS_MOVING(player->jx - 1, player->jy)))
11983 move_dir = MV_LEFT;
11984 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11985 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11986 !IS_MOVING(player->jx + 1, player->jy)))
11987 move_dir = MV_RIGHT;
11989 player->is_sleeping = FALSE;
11991 player->dir_waiting = move_dir;
11994 if (player->is_sleeping)
11996 if (player->num_special_action_sleeping > 0)
11998 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
12000 int last_special_action = player->special_action_sleeping;
12001 int num_special_action = player->num_special_action_sleeping;
12002 int special_action =
12003 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
12004 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
12005 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
12006 last_special_action + 1 : ACTION_SLEEPING);
12007 int special_graphic =
12008 el_act_dir2img(player->artwork_element, special_action, move_dir);
12010 player->anim_delay_counter =
12011 graphic_info[special_graphic].anim_delay_fixed +
12012 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
12013 player->post_delay_counter =
12014 graphic_info[special_graphic].post_delay_fixed +
12015 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
12017 player->special_action_sleeping = special_action;
12020 if (player->anim_delay_counter > 0)
12022 player->action_waiting = player->special_action_sleeping;
12023 player->anim_delay_counter--;
12025 else if (player->post_delay_counter > 0)
12027 player->post_delay_counter--;
12031 else if (player->is_bored)
12033 if (player->num_special_action_bored > 0)
12035 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
12037 int special_action =
12038 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
12039 int special_graphic =
12040 el_act_dir2img(player->artwork_element, special_action, move_dir);
12042 player->anim_delay_counter =
12043 graphic_info[special_graphic].anim_delay_fixed +
12044 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
12045 player->post_delay_counter =
12046 graphic_info[special_graphic].post_delay_fixed +
12047 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
12049 player->special_action_bored = special_action;
12052 if (player->anim_delay_counter > 0)
12054 player->action_waiting = player->special_action_bored;
12055 player->anim_delay_counter--;
12057 else if (player->post_delay_counter > 0)
12059 player->post_delay_counter--;
12064 else if (last_waiting) /* waiting -> not waiting */
12066 player->is_waiting = FALSE;
12067 player->is_bored = FALSE;
12068 player->is_sleeping = FALSE;
12070 player->frame_counter_bored = -1;
12071 player->frame_counter_sleeping = -1;
12073 player->anim_delay_counter = 0;
12074 player->post_delay_counter = 0;
12076 player->dir_waiting = player->MovDir;
12077 player->action_waiting = ACTION_DEFAULT;
12079 player->special_action_bored = ACTION_DEFAULT;
12080 player->special_action_sleeping = ACTION_DEFAULT;
12084 static void CheckSingleStepMode(struct PlayerInfo *player)
12086 if (tape.single_step && tape.recording && !tape.pausing)
12088 /* as it is called "single step mode", just return to pause mode when the
12089 player stopped moving after one tile (or never starts moving at all) */
12090 if (!player->is_moving && !player->is_pushing)
12092 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12093 SnapField(player, 0, 0); /* stop snapping */
12098 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
12100 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
12101 int left = player_action & JOY_LEFT;
12102 int right = player_action & JOY_RIGHT;
12103 int up = player_action & JOY_UP;
12104 int down = player_action & JOY_DOWN;
12105 int button1 = player_action & JOY_BUTTON_1;
12106 int button2 = player_action & JOY_BUTTON_2;
12107 int dx = (left ? -1 : right ? 1 : 0);
12108 int dy = (up ? -1 : down ? 1 : 0);
12110 if (!player->active || tape.pausing)
12116 snapped = SnapField(player, dx, dy);
12120 dropped = DropElement(player);
12122 moved = MovePlayer(player, dx, dy);
12125 CheckSingleStepMode(player);
12127 SetPlayerWaiting(player, FALSE);
12129 return player_action;
12133 /* no actions for this player (no input at player's configured device) */
12135 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
12136 SnapField(player, 0, 0);
12137 CheckGravityMovementWhenNotMoving(player);
12139 if (player->MovPos == 0)
12140 SetPlayerWaiting(player, TRUE);
12142 if (player->MovPos == 0) /* needed for tape.playing */
12143 player->is_moving = FALSE;
12145 player->is_dropping = FALSE;
12146 player->is_dropping_pressed = FALSE;
12147 player->drop_pressed_delay = 0;
12149 CheckSingleStepMode(player);
12155 static void CheckLevelTime()
12159 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
12160 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12162 if (level.native_em_level->lev->home == 0) /* all players at home */
12164 PlayerWins(local_player);
12166 AllPlayersGone = TRUE;
12168 level.native_em_level->lev->home = -1;
12171 if (level.native_em_level->ply[0]->alive == 0 &&
12172 level.native_em_level->ply[1]->alive == 0 &&
12173 level.native_em_level->ply[2]->alive == 0 &&
12174 level.native_em_level->ply[3]->alive == 0) /* all dead */
12175 AllPlayersGone = TRUE;
12177 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12179 if (game_sp.LevelSolved &&
12180 !game_sp.GameOver) /* game won */
12182 PlayerWins(local_player);
12184 game_sp.GameOver = TRUE;
12186 AllPlayersGone = TRUE;
12189 if (game_sp.GameOver) /* game lost */
12190 AllPlayersGone = TRUE;
12193 if (TimeFrames >= FRAMES_PER_SECOND)
12198 for (i = 0; i < MAX_PLAYERS; i++)
12200 struct PlayerInfo *player = &stored_player[i];
12202 if (SHIELD_ON(player))
12204 player->shield_normal_time_left--;
12206 if (player->shield_deadly_time_left > 0)
12207 player->shield_deadly_time_left--;
12211 if (!local_player->LevelSolved && !level.use_step_counter)
12219 if (TimeLeft <= 10 && setup.time_limit)
12220 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12223 /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
12224 is reset from other values in UpdateGameDoorValues() -- FIX THIS */
12226 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12228 /* (already called by UpdateAndDisplayGameControlValues() below) */
12229 // DisplayGameControlValues();
12231 DrawGameValue_Time(TimeLeft);
12234 if (!TimeLeft && setup.time_limit)
12236 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12237 level.native_em_level->lev->killed_out_of_time = TRUE;
12239 for (i = 0; i < MAX_PLAYERS; i++)
12240 KillPlayer(&stored_player[i]);
12244 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12246 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12248 /* (already called by UpdateAndDisplayGameControlValues() below) */
12249 // DisplayGameControlValues();
12252 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12253 DrawGameValue_Time(TimePlayed);
12256 level.native_em_level->lev->time =
12257 (game.no_time_limit ? TimePlayed : TimeLeft);
12260 if (tape.recording || tape.playing)
12261 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12265 UpdateAndDisplayGameControlValues();
12267 UpdateGameDoorValues();
12268 DrawGameDoorValues();
12272 void AdvanceFrameAndPlayerCounters(int player_nr)
12276 /* advance frame counters (global frame counter and time frame counter) */
12280 /* advance player counters (counters for move delay, move animation etc.) */
12281 for (i = 0; i < MAX_PLAYERS; i++)
12283 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12284 int move_delay_value = stored_player[i].move_delay_value;
12285 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12287 if (!advance_player_counters) /* not all players may be affected */
12290 #if USE_NEW_PLAYER_ANIM
12291 if (move_frames == 0) /* less than one move per game frame */
12293 int stepsize = TILEX / move_delay_value;
12294 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12295 int count = (stored_player[i].is_moving ?
12296 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12298 if (count % delay == 0)
12303 stored_player[i].Frame += move_frames;
12305 if (stored_player[i].MovPos != 0)
12306 stored_player[i].StepFrame += move_frames;
12308 if (stored_player[i].move_delay > 0)
12309 stored_player[i].move_delay--;
12311 /* due to bugs in previous versions, counter must count up, not down */
12312 if (stored_player[i].push_delay != -1)
12313 stored_player[i].push_delay++;
12315 if (stored_player[i].drop_delay > 0)
12316 stored_player[i].drop_delay--;
12318 if (stored_player[i].is_dropping_pressed)
12319 stored_player[i].drop_pressed_delay++;
12323 void StartGameActions(boolean init_network_game, boolean record_tape,
12326 unsigned int new_random_seed = InitRND(random_seed);
12329 TapeStartRecording(new_random_seed);
12331 #if defined(NETWORK_AVALIABLE)
12332 if (init_network_game)
12334 SendToServer_StartPlaying();
12345 static unsigned int game_frame_delay = 0;
12346 unsigned int game_frame_delay_value;
12347 byte *recorded_player_action;
12348 byte summarized_player_action = 0;
12349 byte tape_action[MAX_PLAYERS];
12352 /* detect endless loops, caused by custom element programming */
12353 if (recursion_loop_detected && recursion_loop_depth == 0)
12355 char *message = getStringCat3("Internal Error ! Element ",
12356 EL_NAME(recursion_loop_element),
12357 " caused endless loop ! Quit the game ?");
12359 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12360 EL_NAME(recursion_loop_element));
12362 RequestQuitGameExt(FALSE, level_editor_test_game, message);
12364 recursion_loop_detected = FALSE; /* if game should be continued */
12371 if (game.restart_level)
12372 StartGameActions(options.network, setup.autorecord, level.random_seed);
12374 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
12375 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12377 if (level.native_em_level->lev->home == 0) /* all players at home */
12379 PlayerWins(local_player);
12381 AllPlayersGone = TRUE;
12383 level.native_em_level->lev->home = -1;
12386 if (level.native_em_level->ply[0]->alive == 0 &&
12387 level.native_em_level->ply[1]->alive == 0 &&
12388 level.native_em_level->ply[2]->alive == 0 &&
12389 level.native_em_level->ply[3]->alive == 0) /* all dead */
12390 AllPlayersGone = TRUE;
12392 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12394 if (game_sp.LevelSolved &&
12395 !game_sp.GameOver) /* game won */
12397 PlayerWins(local_player);
12399 game_sp.GameOver = TRUE;
12401 AllPlayersGone = TRUE;
12404 if (game_sp.GameOver) /* game lost */
12405 AllPlayersGone = TRUE;
12408 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12411 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12414 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
12417 game_frame_delay_value =
12418 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12420 if (tape.playing && tape.warp_forward && !tape.pausing)
12421 game_frame_delay_value = 0;
12423 /* ---------- main game synchronization point ---------- */
12425 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12427 if (network_playing && !network_player_action_received)
12429 /* try to get network player actions in time */
12431 #if defined(NETWORK_AVALIABLE)
12432 /* last chance to get network player actions without main loop delay */
12433 HandleNetworking();
12436 /* game was quit by network peer */
12437 if (game_status != GAME_MODE_PLAYING)
12440 if (!network_player_action_received)
12441 return; /* failed to get network player actions in time */
12443 /* do not yet reset "network_player_action_received" (for tape.pausing) */
12449 /* at this point we know that we really continue executing the game */
12451 network_player_action_received = FALSE;
12453 /* when playing tape, read previously recorded player input from tape data */
12454 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12457 /* TapePlayAction() may return NULL when toggling to "pause before death" */
12462 if (tape.set_centered_player)
12464 game.centered_player_nr_next = tape.centered_player_nr_next;
12465 game.set_centered_player = TRUE;
12468 for (i = 0; i < MAX_PLAYERS; i++)
12470 summarized_player_action |= stored_player[i].action;
12472 if (!network_playing)
12473 stored_player[i].effective_action = stored_player[i].action;
12476 #if defined(NETWORK_AVALIABLE)
12477 if (network_playing)
12478 SendToServer_MovePlayer(summarized_player_action);
12481 if (!options.network && !setup.team_mode)
12482 local_player->effective_action = summarized_player_action;
12484 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
12486 for (i = 0; i < MAX_PLAYERS; i++)
12487 stored_player[i].effective_action =
12488 (i == game.centered_player_nr ? summarized_player_action : 0);
12491 if (recorded_player_action != NULL)
12492 for (i = 0; i < MAX_PLAYERS; i++)
12493 stored_player[i].effective_action = recorded_player_action[i];
12495 for (i = 0; i < MAX_PLAYERS; i++)
12497 tape_action[i] = stored_player[i].effective_action;
12499 /* (this can only happen in the R'n'D game engine) */
12500 if (tape.recording && tape_action[i] && !tape.player_participates[i])
12501 tape.player_participates[i] = TRUE; /* player just appeared from CE */
12504 /* only record actions from input devices, but not programmed actions */
12505 if (tape.recording)
12506 TapeRecordAction(tape_action);
12508 #if USE_NEW_PLAYER_ASSIGNMENTS
12510 byte mapped_action[MAX_PLAYERS];
12512 for (i = 0; i < MAX_PLAYERS; i++)
12513 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12515 for (i = 0; i < MAX_PLAYERS; i++)
12516 stored_player[i].effective_action = mapped_action[i];
12520 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12522 GameActions_EM_Main();
12524 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12526 GameActions_SP_Main();
12534 void GameActions_EM_Main()
12536 byte effective_action[MAX_PLAYERS];
12537 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12540 for (i = 0; i < MAX_PLAYERS; i++)
12541 effective_action[i] = stored_player[i].effective_action;
12543 GameActions_EM(effective_action, warp_mode);
12547 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12550 void GameActions_SP_Main()
12552 byte effective_action[MAX_PLAYERS];
12553 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12556 for (i = 0; i < MAX_PLAYERS; i++)
12557 effective_action[i] = stored_player[i].effective_action;
12559 GameActions_SP(effective_action, warp_mode);
12563 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12566 void GameActions_RND()
12568 int magic_wall_x = 0, magic_wall_y = 0;
12569 int i, x, y, element, graphic;
12571 InitPlayfieldScanModeVars();
12573 #if USE_ONE_MORE_CHANGE_PER_FRAME
12574 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12576 SCAN_PLAYFIELD(x, y)
12578 ChangeCount[x][y] = 0;
12579 ChangeEvent[x][y] = -1;
12584 if (game.set_centered_player)
12586 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12588 /* switching to "all players" only possible if all players fit to screen */
12589 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12591 game.centered_player_nr_next = game.centered_player_nr;
12592 game.set_centered_player = FALSE;
12595 /* do not switch focus to non-existing (or non-active) player */
12596 if (game.centered_player_nr_next >= 0 &&
12597 !stored_player[game.centered_player_nr_next].active)
12599 game.centered_player_nr_next = game.centered_player_nr;
12600 game.set_centered_player = FALSE;
12604 if (game.set_centered_player &&
12605 ScreenMovPos == 0) /* screen currently aligned at tile position */
12609 if (game.centered_player_nr_next == -1)
12611 setScreenCenteredToAllPlayers(&sx, &sy);
12615 sx = stored_player[game.centered_player_nr_next].jx;
12616 sy = stored_player[game.centered_player_nr_next].jy;
12619 game.centered_player_nr = game.centered_player_nr_next;
12620 game.set_centered_player = FALSE;
12622 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12623 DrawGameDoorValues();
12626 for (i = 0; i < MAX_PLAYERS; i++)
12628 int actual_player_action = stored_player[i].effective_action;
12631 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12632 - rnd_equinox_tetrachloride 048
12633 - rnd_equinox_tetrachloride_ii 096
12634 - rnd_emanuel_schmieg 002
12635 - doctor_sloan_ww 001, 020
12637 if (stored_player[i].MovPos == 0)
12638 CheckGravityMovement(&stored_player[i]);
12641 /* overwrite programmed action with tape action */
12642 if (stored_player[i].programmed_action)
12643 actual_player_action = stored_player[i].programmed_action;
12645 PlayerActions(&stored_player[i], actual_player_action);
12647 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12650 ScrollScreen(NULL, SCROLL_GO_ON);
12652 /* for backwards compatibility, the following code emulates a fixed bug that
12653 occured when pushing elements (causing elements that just made their last
12654 pushing step to already (if possible) make their first falling step in the
12655 same game frame, which is bad); this code is also needed to use the famous
12656 "spring push bug" which is used in older levels and might be wanted to be
12657 used also in newer levels, but in this case the buggy pushing code is only
12658 affecting the "spring" element and no other elements */
12660 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12662 for (i = 0; i < MAX_PLAYERS; i++)
12664 struct PlayerInfo *player = &stored_player[i];
12665 int x = player->jx;
12666 int y = player->jy;
12668 if (player->active && player->is_pushing && player->is_moving &&
12670 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12671 Feld[x][y] == EL_SPRING))
12673 ContinueMoving(x, y);
12675 /* continue moving after pushing (this is actually a bug) */
12676 if (!IS_MOVING(x, y))
12677 Stop[x][y] = FALSE;
12683 debug_print_timestamp(0, "start main loop profiling");
12686 SCAN_PLAYFIELD(x, y)
12688 ChangeCount[x][y] = 0;
12689 ChangeEvent[x][y] = -1;
12691 /* this must be handled before main playfield loop */
12692 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12695 if (MovDelay[x][y] <= 0)
12699 #if USE_NEW_SNAP_DELAY
12700 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12703 if (MovDelay[x][y] <= 0)
12706 TEST_DrawLevelField(x, y);
12708 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12714 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12716 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12717 printf("GameActions(): This should never happen!\n");
12719 ChangePage[x][y] = -1;
12723 Stop[x][y] = FALSE;
12724 if (WasJustMoving[x][y] > 0)
12725 WasJustMoving[x][y]--;
12726 if (WasJustFalling[x][y] > 0)
12727 WasJustFalling[x][y]--;
12728 if (CheckCollision[x][y] > 0)
12729 CheckCollision[x][y]--;
12730 if (CheckImpact[x][y] > 0)
12731 CheckImpact[x][y]--;
12735 /* reset finished pushing action (not done in ContinueMoving() to allow
12736 continuous pushing animation for elements with zero push delay) */
12737 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12739 ResetGfxAnimation(x, y);
12740 TEST_DrawLevelField(x, y);
12744 if (IS_BLOCKED(x, y))
12748 Blocked2Moving(x, y, &oldx, &oldy);
12749 if (!IS_MOVING(oldx, oldy))
12751 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12752 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12753 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12754 printf("GameActions(): This should never happen!\n");
12761 debug_print_timestamp(0, "- time for pre-main loop:");
12764 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
12765 SCAN_PLAYFIELD(x, y)
12767 element = Feld[x][y];
12768 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12773 int element2 = element;
12774 int graphic2 = graphic;
12776 int element2 = Feld[x][y];
12777 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12779 int last_gfx_frame = GfxFrame[x][y];
12781 if (graphic_info[graphic2].anim_global_sync)
12782 GfxFrame[x][y] = FrameCounter;
12783 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12784 GfxFrame[x][y] = CustomValue[x][y];
12785 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12786 GfxFrame[x][y] = element_info[element2].collect_score;
12787 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12788 GfxFrame[x][y] = ChangeDelay[x][y];
12790 if (redraw && GfxFrame[x][y] != last_gfx_frame)
12791 DrawLevelGraphicAnimation(x, y, graphic2);
12794 ResetGfxFrame(x, y, TRUE);
12798 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12799 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12800 ResetRandomAnimationValue(x, y);
12804 SetRandomAnimationValue(x, y);
12808 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12811 #endif // -------------------- !!! TEST ONLY !!! --------------------
12814 debug_print_timestamp(0, "- time for TEST loop: -->");
12817 SCAN_PLAYFIELD(x, y)
12819 element = Feld[x][y];
12820 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12822 ResetGfxFrame(x, y, TRUE);
12824 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12825 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12826 ResetRandomAnimationValue(x, y);
12828 SetRandomAnimationValue(x, y);
12830 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12832 if (IS_INACTIVE(element))
12834 if (IS_ANIMATED(graphic))
12835 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12840 /* this may take place after moving, so 'element' may have changed */
12841 if (IS_CHANGING(x, y) &&
12842 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12844 int page = element_info[element].event_page_nr[CE_DELAY];
12847 HandleElementChange(x, y, page);
12849 if (CAN_CHANGE(element))
12850 HandleElementChange(x, y, page);
12852 if (HAS_ACTION(element))
12853 ExecuteCustomElementAction(x, y, element, page);
12856 element = Feld[x][y];
12857 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12860 #if 0 // ---------------------------------------------------------------------
12862 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12866 element = Feld[x][y];
12867 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12869 if (IS_ANIMATED(graphic) &&
12870 !IS_MOVING(x, y) &&
12872 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12874 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12875 TEST_DrawTwinkleOnField(x, y);
12877 else if (IS_MOVING(x, y))
12878 ContinueMoving(x, y);
12885 case EL_EM_EXIT_OPEN:
12886 case EL_SP_EXIT_OPEN:
12887 case EL_STEEL_EXIT_OPEN:
12888 case EL_EM_STEEL_EXIT_OPEN:
12889 case EL_SP_TERMINAL:
12890 case EL_SP_TERMINAL_ACTIVE:
12891 case EL_EXTRA_TIME:
12892 case EL_SHIELD_NORMAL:
12893 case EL_SHIELD_DEADLY:
12894 if (IS_ANIMATED(graphic))
12895 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12898 case EL_DYNAMITE_ACTIVE:
12899 case EL_EM_DYNAMITE_ACTIVE:
12900 case EL_DYNABOMB_PLAYER_1_ACTIVE:
12901 case EL_DYNABOMB_PLAYER_2_ACTIVE:
12902 case EL_DYNABOMB_PLAYER_3_ACTIVE:
12903 case EL_DYNABOMB_PLAYER_4_ACTIVE:
12904 case EL_SP_DISK_RED_ACTIVE:
12905 CheckDynamite(x, y);
12908 case EL_AMOEBA_GROWING:
12909 AmoebeWaechst(x, y);
12912 case EL_AMOEBA_SHRINKING:
12913 AmoebaDisappearing(x, y);
12916 #if !USE_NEW_AMOEBA_CODE
12917 case EL_AMOEBA_WET:
12918 case EL_AMOEBA_DRY:
12919 case EL_AMOEBA_FULL:
12921 case EL_EMC_DRIPPER:
12922 AmoebeAbleger(x, y);
12926 case EL_GAME_OF_LIFE:
12931 case EL_EXIT_CLOSED:
12935 case EL_EM_EXIT_CLOSED:
12939 case EL_STEEL_EXIT_CLOSED:
12940 CheckExitSteel(x, y);
12943 case EL_EM_STEEL_EXIT_CLOSED:
12944 CheckExitSteelEM(x, y);
12947 case EL_SP_EXIT_CLOSED:
12951 case EL_EXPANDABLE_WALL_GROWING:
12952 case EL_EXPANDABLE_STEELWALL_GROWING:
12953 MauerWaechst(x, y);
12956 case EL_EXPANDABLE_WALL:
12957 case EL_EXPANDABLE_WALL_HORIZONTAL:
12958 case EL_EXPANDABLE_WALL_VERTICAL:
12959 case EL_EXPANDABLE_WALL_ANY:
12960 case EL_BD_EXPANDABLE_WALL:
12961 MauerAbleger(x, y);
12964 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12965 case EL_EXPANDABLE_STEELWALL_VERTICAL:
12966 case EL_EXPANDABLE_STEELWALL_ANY:
12967 MauerAblegerStahl(x, y);
12971 CheckForDragon(x, y);
12977 case EL_ELEMENT_SNAPPING:
12978 case EL_DIAGONAL_SHRINKING:
12979 case EL_DIAGONAL_GROWING:
12982 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12984 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12989 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12990 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12995 #else // ---------------------------------------------------------------------
12997 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
13001 element = Feld[x][y];
13002 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
13004 if (IS_ANIMATED(graphic) &&
13005 !IS_MOVING(x, y) &&
13007 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13009 if (IS_GEM(element) || element == EL_SP_INFOTRON)
13010 TEST_DrawTwinkleOnField(x, y);
13012 else if ((element == EL_ACID ||
13013 element == EL_EXIT_OPEN ||
13014 element == EL_EM_EXIT_OPEN ||
13015 element == EL_SP_EXIT_OPEN ||
13016 element == EL_STEEL_EXIT_OPEN ||
13017 element == EL_EM_STEEL_EXIT_OPEN ||
13018 element == EL_SP_TERMINAL ||
13019 element == EL_SP_TERMINAL_ACTIVE ||
13020 element == EL_EXTRA_TIME ||
13021 element == EL_SHIELD_NORMAL ||
13022 element == EL_SHIELD_DEADLY) &&
13023 IS_ANIMATED(graphic))
13024 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13025 else if (IS_MOVING(x, y))
13026 ContinueMoving(x, y);
13027 else if (IS_ACTIVE_BOMB(element))
13028 CheckDynamite(x, y);
13029 else if (element == EL_AMOEBA_GROWING)
13030 AmoebeWaechst(x, y);
13031 else if (element == EL_AMOEBA_SHRINKING)
13032 AmoebaDisappearing(x, y);
13034 #if !USE_NEW_AMOEBA_CODE
13035 else if (IS_AMOEBALIVE(element))
13036 AmoebeAbleger(x, y);
13039 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
13041 else if (element == EL_EXIT_CLOSED)
13043 else if (element == EL_EM_EXIT_CLOSED)
13045 else if (element == EL_STEEL_EXIT_CLOSED)
13046 CheckExitSteel(x, y);
13047 else if (element == EL_EM_STEEL_EXIT_CLOSED)
13048 CheckExitSteelEM(x, y);
13049 else if (element == EL_SP_EXIT_CLOSED)
13051 else if (element == EL_EXPANDABLE_WALL_GROWING ||
13052 element == EL_EXPANDABLE_STEELWALL_GROWING)
13053 MauerWaechst(x, y);
13054 else if (element == EL_EXPANDABLE_WALL ||
13055 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
13056 element == EL_EXPANDABLE_WALL_VERTICAL ||
13057 element == EL_EXPANDABLE_WALL_ANY ||
13058 element == EL_BD_EXPANDABLE_WALL)
13059 MauerAbleger(x, y);
13060 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
13061 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
13062 element == EL_EXPANDABLE_STEELWALL_ANY)
13063 MauerAblegerStahl(x, y);
13064 else if (element == EL_FLAMES)
13065 CheckForDragon(x, y);
13066 else if (element == EL_EXPLOSION)
13067 ; /* drawing of correct explosion animation is handled separately */
13068 else if (element == EL_ELEMENT_SNAPPING ||
13069 element == EL_DIAGONAL_SHRINKING ||
13070 element == EL_DIAGONAL_GROWING)
13072 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
13074 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13076 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
13077 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13079 #endif // ---------------------------------------------------------------------
13081 if (IS_BELT_ACTIVE(element))
13082 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
13084 if (game.magic_wall_active)
13086 int jx = local_player->jx, jy = local_player->jy;
13088 /* play the element sound at the position nearest to the player */
13089 if ((element == EL_MAGIC_WALL_FULL ||
13090 element == EL_MAGIC_WALL_ACTIVE ||
13091 element == EL_MAGIC_WALL_EMPTYING ||
13092 element == EL_BD_MAGIC_WALL_FULL ||
13093 element == EL_BD_MAGIC_WALL_ACTIVE ||
13094 element == EL_BD_MAGIC_WALL_EMPTYING ||
13095 element == EL_DC_MAGIC_WALL_FULL ||
13096 element == EL_DC_MAGIC_WALL_ACTIVE ||
13097 element == EL_DC_MAGIC_WALL_EMPTYING) &&
13098 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
13107 debug_print_timestamp(0, "- time for MAIN loop: -->");
13110 #if USE_NEW_AMOEBA_CODE
13111 /* new experimental amoeba growth stuff */
13112 if (!(FrameCounter % 8))
13114 static unsigned int random = 1684108901;
13116 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
13118 x = RND(lev_fieldx);
13119 y = RND(lev_fieldy);
13120 element = Feld[x][y];
13122 if (!IS_PLAYER(x,y) &&
13123 (element == EL_EMPTY ||
13124 CAN_GROW_INTO(element) ||
13125 element == EL_QUICKSAND_EMPTY ||
13126 element == EL_QUICKSAND_FAST_EMPTY ||
13127 element == EL_ACID_SPLASH_LEFT ||
13128 element == EL_ACID_SPLASH_RIGHT))
13130 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
13131 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
13132 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
13133 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
13134 Feld[x][y] = EL_AMOEBA_DROP;
13137 random = random * 129 + 1;
13143 if (game.explosions_delayed)
13146 game.explosions_delayed = FALSE;
13148 SCAN_PLAYFIELD(x, y)
13150 element = Feld[x][y];
13152 if (ExplodeField[x][y])
13153 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
13154 else if (element == EL_EXPLOSION)
13155 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
13157 ExplodeField[x][y] = EX_TYPE_NONE;
13160 game.explosions_delayed = TRUE;
13163 if (game.magic_wall_active)
13165 if (!(game.magic_wall_time_left % 4))
13167 int element = Feld[magic_wall_x][magic_wall_y];
13169 if (element == EL_BD_MAGIC_WALL_FULL ||
13170 element == EL_BD_MAGIC_WALL_ACTIVE ||
13171 element == EL_BD_MAGIC_WALL_EMPTYING)
13172 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
13173 else if (element == EL_DC_MAGIC_WALL_FULL ||
13174 element == EL_DC_MAGIC_WALL_ACTIVE ||
13175 element == EL_DC_MAGIC_WALL_EMPTYING)
13176 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
13178 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
13181 if (game.magic_wall_time_left > 0)
13183 game.magic_wall_time_left--;
13185 if (!game.magic_wall_time_left)
13187 SCAN_PLAYFIELD(x, y)
13189 element = Feld[x][y];
13191 if (element == EL_MAGIC_WALL_ACTIVE ||
13192 element == EL_MAGIC_WALL_FULL)
13194 Feld[x][y] = EL_MAGIC_WALL_DEAD;
13195 TEST_DrawLevelField(x, y);
13197 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
13198 element == EL_BD_MAGIC_WALL_FULL)
13200 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
13201 TEST_DrawLevelField(x, y);
13203 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
13204 element == EL_DC_MAGIC_WALL_FULL)
13206 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
13207 TEST_DrawLevelField(x, y);
13211 game.magic_wall_active = FALSE;
13216 if (game.light_time_left > 0)
13218 game.light_time_left--;
13220 if (game.light_time_left == 0)
13221 RedrawAllLightSwitchesAndInvisibleElements();
13224 if (game.timegate_time_left > 0)
13226 game.timegate_time_left--;
13228 if (game.timegate_time_left == 0)
13229 CloseAllOpenTimegates();
13232 if (game.lenses_time_left > 0)
13234 game.lenses_time_left--;
13236 if (game.lenses_time_left == 0)
13237 RedrawAllInvisibleElementsForLenses();
13240 if (game.magnify_time_left > 0)
13242 game.magnify_time_left--;
13244 if (game.magnify_time_left == 0)
13245 RedrawAllInvisibleElementsForMagnifier();
13248 for (i = 0; i < MAX_PLAYERS; i++)
13250 struct PlayerInfo *player = &stored_player[i];
13252 if (SHIELD_ON(player))
13254 if (player->shield_deadly_time_left)
13255 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13256 else if (player->shield_normal_time_left)
13257 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13261 #if USE_DELAYED_GFX_REDRAW
13262 SCAN_PLAYFIELD(x, y)
13265 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13267 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13268 GfxRedraw[x][y] != GFX_REDRAW_NONE)
13271 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13272 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13274 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13275 DrawLevelField(x, y);
13277 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13278 DrawLevelFieldCrumbled(x, y);
13280 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13281 DrawLevelFieldCrumbledNeighbours(x, y);
13283 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13284 DrawTwinkleOnField(x, y);
13287 GfxRedraw[x][y] = GFX_REDRAW_NONE;
13294 PlayAllPlayersSound();
13296 if (options.debug) /* calculate frames per second */
13298 static unsigned int fps_counter = 0;
13299 static int fps_frames = 0;
13300 unsigned int fps_delay_ms = Counter() - fps_counter;
13304 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
13306 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13309 fps_counter = Counter();
13312 redraw_mask |= REDRAW_FPS;
13315 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
13317 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13319 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13321 local_player->show_envelope = 0;
13325 debug_print_timestamp(0, "stop main loop profiling ");
13326 printf("----------------------------------------------------------\n");
13329 /* use random number generator in every frame to make it less predictable */
13330 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13334 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13336 int min_x = x, min_y = y, max_x = x, max_y = y;
13339 for (i = 0; i < MAX_PLAYERS; i++)
13341 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13343 if (!stored_player[i].active || &stored_player[i] == player)
13346 min_x = MIN(min_x, jx);
13347 min_y = MIN(min_y, jy);
13348 max_x = MAX(max_x, jx);
13349 max_y = MAX(max_y, jy);
13352 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13355 static boolean AllPlayersInVisibleScreen()
13359 for (i = 0; i < MAX_PLAYERS; i++)
13361 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13363 if (!stored_player[i].active)
13366 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13373 void ScrollLevel(int dx, int dy)
13376 /* (directly solved in BlitBitmap() now) */
13377 static Bitmap *bitmap_db_field2 = NULL;
13378 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13385 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13386 /* only horizontal XOR vertical scroll direction allowed */
13387 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13392 /* (directly solved in BlitBitmap() now) */
13393 if (bitmap_db_field2 == NULL)
13394 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13396 /* needed when blitting directly to same bitmap -- should not be needed with
13397 recent SDL libraries, but apparently does not work in 1.2.11 directly */
13398 BlitBitmap(drawto_field, bitmap_db_field2,
13399 FX + TILEX * (dx == -1) - softscroll_offset,
13400 FY + TILEY * (dy == -1) - softscroll_offset,
13401 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13402 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13403 FX + TILEX * (dx == 1) - softscroll_offset,
13404 FY + TILEY * (dy == 1) - softscroll_offset);
13405 BlitBitmap(bitmap_db_field2, drawto_field,
13406 FX + TILEX * (dx == 1) - softscroll_offset,
13407 FY + TILEY * (dy == 1) - softscroll_offset,
13408 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13409 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13410 FX + TILEX * (dx == 1) - softscroll_offset,
13411 FY + TILEY * (dy == 1) - softscroll_offset);
13416 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13417 int xsize = (BX2 - BX1 + 1);
13418 int ysize = (BY2 - BY1 + 1);
13419 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13420 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13421 int step = (start < end ? +1 : -1);
13423 for (i = start; i != end; i += step)
13425 BlitBitmap(drawto_field, drawto_field,
13426 FX + TILEX * (dx != 0 ? i + step : 0),
13427 FY + TILEY * (dy != 0 ? i + step : 0),
13428 TILEX * (dx != 0 ? 1 : xsize),
13429 TILEY * (dy != 0 ? 1 : ysize),
13430 FX + TILEX * (dx != 0 ? i : 0),
13431 FY + TILEY * (dy != 0 ? i : 0));
13438 int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX_VAR : 0);
13440 int softscroll_offset = (setup.soft_scrolling ? TILEX_VAR : 0);
13444 int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX : 0);
13446 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13451 BlitBitmap(drawto_field, drawto_field,
13452 FX + TILEX_VAR * (dx == -1) - softscroll_offset,
13453 FY + TILEY_VAR * (dy == -1) - softscroll_offset,
13454 SXSIZE - TILEX_VAR * (dx != 0) + 2 * softscroll_offset,
13455 SYSIZE - TILEY_VAR * (dy != 0) + 2 * softscroll_offset,
13456 FX + TILEX_VAR * (dx == 1) - softscroll_offset,
13457 FY + TILEY_VAR * (dy == 1) - softscroll_offset);
13459 BlitBitmap(drawto_field, drawto_field,
13460 FX + TILEX * (dx == -1) - softscroll_offset,
13461 FY + TILEY * (dy == -1) - softscroll_offset,
13462 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13463 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13464 FX + TILEX * (dx == 1) - softscroll_offset,
13465 FY + TILEY * (dy == 1) - softscroll_offset);
13473 x = (dx == 1 ? BX1 : BX2);
13474 for (y = BY1; y <= BY2; y++)
13475 DrawScreenField(x, y);
13480 y = (dy == 1 ? BY1 : BY2);
13481 for (x = BX1; x <= BX2; x++)
13482 DrawScreenField(x, y);
13485 redraw_mask |= REDRAW_FIELD;
13488 static boolean canFallDown(struct PlayerInfo *player)
13490 int jx = player->jx, jy = player->jy;
13492 return (IN_LEV_FIELD(jx, jy + 1) &&
13493 (IS_FREE(jx, jy + 1) ||
13494 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13495 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13496 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13499 static boolean canPassField(int x, int y, int move_dir)
13501 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13502 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13503 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13504 int nextx = x + dx;
13505 int nexty = y + dy;
13506 int element = Feld[x][y];
13508 return (IS_PASSABLE_FROM(element, opposite_dir) &&
13509 !CAN_MOVE(element) &&
13510 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13511 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13512 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13515 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13517 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13518 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13519 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13523 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13524 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13525 (IS_DIGGABLE(Feld[newx][newy]) ||
13526 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13527 canPassField(newx, newy, move_dir)));
13530 static void CheckGravityMovement(struct PlayerInfo *player)
13532 #if USE_PLAYER_GRAVITY
13533 if (player->gravity && !player->programmed_action)
13535 if (game.gravity && !player->programmed_action)
13538 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13539 int move_dir_vertical = player->effective_action & MV_VERTICAL;
13540 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13541 int jx = player->jx, jy = player->jy;
13542 boolean player_is_moving_to_valid_field =
13543 (!player_is_snapping &&
13544 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13545 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13546 boolean player_can_fall_down = canFallDown(player);
13548 if (player_can_fall_down &&
13549 !player_is_moving_to_valid_field)
13550 player->programmed_action = MV_DOWN;
13554 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13556 return CheckGravityMovement(player);
13558 #if USE_PLAYER_GRAVITY
13559 if (player->gravity && !player->programmed_action)
13561 if (game.gravity && !player->programmed_action)
13564 int jx = player->jx, jy = player->jy;
13565 boolean field_under_player_is_free =
13566 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13567 boolean player_is_standing_on_valid_field =
13568 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13569 (IS_WALKABLE(Feld[jx][jy]) &&
13570 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13572 if (field_under_player_is_free && !player_is_standing_on_valid_field)
13573 player->programmed_action = MV_DOWN;
13578 MovePlayerOneStep()
13579 -----------------------------------------------------------------------------
13580 dx, dy: direction (non-diagonal) to try to move the player to
13581 real_dx, real_dy: direction as read from input device (can be diagonal)
13584 boolean MovePlayerOneStep(struct PlayerInfo *player,
13585 int dx, int dy, int real_dx, int real_dy)
13587 int jx = player->jx, jy = player->jy;
13588 int new_jx = jx + dx, new_jy = jy + dy;
13589 #if !USE_FIXED_DONT_RUN_INTO
13593 boolean player_can_move = !player->cannot_move;
13595 if (!player->active || (!dx && !dy))
13596 return MP_NO_ACTION;
13598 player->MovDir = (dx < 0 ? MV_LEFT :
13599 dx > 0 ? MV_RIGHT :
13601 dy > 0 ? MV_DOWN : MV_NONE);
13603 if (!IN_LEV_FIELD(new_jx, new_jy))
13604 return MP_NO_ACTION;
13606 if (!player_can_move)
13608 if (player->MovPos == 0)
13610 player->is_moving = FALSE;
13611 player->is_digging = FALSE;
13612 player->is_collecting = FALSE;
13613 player->is_snapping = FALSE;
13614 player->is_pushing = FALSE;
13619 if (!options.network && game.centered_player_nr == -1 &&
13620 !AllPlayersInSight(player, new_jx, new_jy))
13621 return MP_NO_ACTION;
13623 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13624 return MP_NO_ACTION;
13627 #if !USE_FIXED_DONT_RUN_INTO
13628 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13630 /* (moved to DigField()) */
13631 if (player_can_move && DONT_RUN_INTO(element))
13633 if (element == EL_ACID && dx == 0 && dy == 1)
13635 SplashAcid(new_jx, new_jy);
13636 Feld[jx][jy] = EL_PLAYER_1;
13637 InitMovingField(jx, jy, MV_DOWN);
13638 Store[jx][jy] = EL_ACID;
13639 ContinueMoving(jx, jy);
13640 BuryPlayer(player);
13643 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13649 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13650 if (can_move != MP_MOVING)
13653 /* check if DigField() has caused relocation of the player */
13654 if (player->jx != jx || player->jy != jy)
13655 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13657 StorePlayer[jx][jy] = 0;
13658 player->last_jx = jx;
13659 player->last_jy = jy;
13660 player->jx = new_jx;
13661 player->jy = new_jy;
13662 StorePlayer[new_jx][new_jy] = player->element_nr;
13664 if (player->move_delay_value_next != -1)
13666 player->move_delay_value = player->move_delay_value_next;
13667 player->move_delay_value_next = -1;
13671 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13673 player->step_counter++;
13675 PlayerVisit[jx][jy] = FrameCounter;
13677 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13678 player->is_moving = TRUE;
13682 /* should better be called in MovePlayer(), but this breaks some tapes */
13683 ScrollPlayer(player, SCROLL_INIT);
13689 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13691 int jx = player->jx, jy = player->jy;
13692 int old_jx = jx, old_jy = jy;
13693 int moved = MP_NO_ACTION;
13695 if (!player->active)
13700 if (player->MovPos == 0)
13702 player->is_moving = FALSE;
13703 player->is_digging = FALSE;
13704 player->is_collecting = FALSE;
13705 player->is_snapping = FALSE;
13706 player->is_pushing = FALSE;
13712 if (player->move_delay > 0)
13715 player->move_delay = -1; /* set to "uninitialized" value */
13717 /* store if player is automatically moved to next field */
13718 player->is_auto_moving = (player->programmed_action != MV_NONE);
13720 /* remove the last programmed player action */
13721 player->programmed_action = 0;
13723 if (player->MovPos)
13725 /* should only happen if pre-1.2 tape recordings are played */
13726 /* this is only for backward compatibility */
13728 int original_move_delay_value = player->move_delay_value;
13731 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
13735 /* scroll remaining steps with finest movement resolution */
13736 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13738 while (player->MovPos)
13740 ScrollPlayer(player, SCROLL_GO_ON);
13741 ScrollScreen(NULL, SCROLL_GO_ON);
13743 AdvanceFrameAndPlayerCounters(player->index_nr);
13749 player->move_delay_value = original_move_delay_value;
13752 player->is_active = FALSE;
13754 if (player->last_move_dir & MV_HORIZONTAL)
13756 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13757 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13761 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13762 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13765 #if USE_FIXED_BORDER_RUNNING_GFX
13766 if (!moved && !player->is_active)
13768 player->is_moving = FALSE;
13769 player->is_digging = FALSE;
13770 player->is_collecting = FALSE;
13771 player->is_snapping = FALSE;
13772 player->is_pushing = FALSE;
13780 if (moved & MP_MOVING && !ScreenMovPos &&
13781 (player->index_nr == game.centered_player_nr ||
13782 game.centered_player_nr == -1))
13784 if (moved & MP_MOVING && !ScreenMovPos &&
13785 (player == local_player || !options.network))
13788 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13789 int offset = game.scroll_delay_value;
13791 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13793 /* actual player has left the screen -- scroll in that direction */
13794 if (jx != old_jx) /* player has moved horizontally */
13795 scroll_x += (jx - old_jx);
13796 else /* player has moved vertically */
13797 scroll_y += (jy - old_jy);
13801 if (jx != old_jx) /* player has moved horizontally */
13803 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
13804 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13805 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13807 /* don't scroll over playfield boundaries */
13808 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13809 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13811 /* don't scroll more than one field at a time */
13812 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13814 /* don't scroll against the player's moving direction */
13815 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
13816 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13817 scroll_x = old_scroll_x;
13819 else /* player has moved vertically */
13821 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
13822 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13823 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13825 /* don't scroll over playfield boundaries */
13826 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13827 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13829 /* don't scroll more than one field at a time */
13830 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13832 /* don't scroll against the player's moving direction */
13833 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
13834 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13835 scroll_y = old_scroll_y;
13839 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13842 if (!options.network && game.centered_player_nr == -1 &&
13843 !AllPlayersInVisibleScreen())
13845 scroll_x = old_scroll_x;
13846 scroll_y = old_scroll_y;
13850 if (!options.network && !AllPlayersInVisibleScreen())
13852 scroll_x = old_scroll_x;
13853 scroll_y = old_scroll_y;
13858 ScrollScreen(player, SCROLL_INIT);
13859 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13864 player->StepFrame = 0;
13866 if (moved & MP_MOVING)
13868 if (old_jx != jx && old_jy == jy)
13869 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13870 else if (old_jx == jx && old_jy != jy)
13871 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13873 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
13875 player->last_move_dir = player->MovDir;
13876 player->is_moving = TRUE;
13877 player->is_snapping = FALSE;
13878 player->is_switching = FALSE;
13879 player->is_dropping = FALSE;
13880 player->is_dropping_pressed = FALSE;
13881 player->drop_pressed_delay = 0;
13884 /* should better be called here than above, but this breaks some tapes */
13885 ScrollPlayer(player, SCROLL_INIT);
13890 CheckGravityMovementWhenNotMoving(player);
13892 player->is_moving = FALSE;
13894 /* at this point, the player is allowed to move, but cannot move right now
13895 (e.g. because of something blocking the way) -- ensure that the player
13896 is also allowed to move in the next frame (in old versions before 3.1.1,
13897 the player was forced to wait again for eight frames before next try) */
13899 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13900 player->move_delay = 0; /* allow direct movement in the next frame */
13903 if (player->move_delay == -1) /* not yet initialized by DigField() */
13904 player->move_delay = player->move_delay_value;
13906 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13908 TestIfPlayerTouchesBadThing(jx, jy);
13909 TestIfPlayerTouchesCustomElement(jx, jy);
13912 if (!player->active)
13913 RemovePlayer(player);
13918 void ScrollPlayer(struct PlayerInfo *player, int mode)
13920 int jx = player->jx, jy = player->jy;
13921 int last_jx = player->last_jx, last_jy = player->last_jy;
13922 int move_stepsize = TILEX / player->move_delay_value;
13924 #if USE_NEW_PLAYER_SPEED
13925 if (!player->active)
13928 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
13931 if (!player->active || player->MovPos == 0)
13935 if (mode == SCROLL_INIT)
13937 player->actual_frame_counter = FrameCounter;
13938 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13940 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13941 Feld[last_jx][last_jy] == EL_EMPTY)
13943 int last_field_block_delay = 0; /* start with no blocking at all */
13944 int block_delay_adjustment = player->block_delay_adjustment;
13946 /* if player blocks last field, add delay for exactly one move */
13947 if (player->block_last_field)
13949 last_field_block_delay += player->move_delay_value;
13951 /* when blocking enabled, prevent moving up despite gravity */
13952 #if USE_PLAYER_GRAVITY
13953 if (player->gravity && player->MovDir == MV_UP)
13954 block_delay_adjustment = -1;
13956 if (game.gravity && player->MovDir == MV_UP)
13957 block_delay_adjustment = -1;
13961 /* add block delay adjustment (also possible when not blocking) */
13962 last_field_block_delay += block_delay_adjustment;
13964 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13965 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13968 #if USE_NEW_PLAYER_SPEED
13969 if (player->MovPos != 0) /* player has not yet reached destination */
13975 else if (!FrameReached(&player->actual_frame_counter, 1))
13978 #if USE_NEW_PLAYER_SPEED
13979 if (player->MovPos != 0)
13981 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13982 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13984 /* before DrawPlayer() to draw correct player graphic for this case */
13985 if (player->MovPos == 0)
13986 CheckGravityMovement(player);
13989 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13990 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13992 /* before DrawPlayer() to draw correct player graphic for this case */
13993 if (player->MovPos == 0)
13994 CheckGravityMovement(player);
13997 if (player->MovPos == 0) /* player reached destination field */
13999 if (player->move_delay_reset_counter > 0)
14001 player->move_delay_reset_counter--;
14003 if (player->move_delay_reset_counter == 0)
14005 /* continue with normal speed after quickly moving through gate */
14006 HALVE_PLAYER_SPEED(player);
14008 /* be able to make the next move without delay */
14009 player->move_delay = 0;
14013 player->last_jx = jx;
14014 player->last_jy = jy;
14016 if (Feld[jx][jy] == EL_EXIT_OPEN ||
14017 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
14019 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
14021 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
14022 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
14024 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
14026 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
14027 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
14029 DrawPlayer(player); /* needed here only to cleanup last field */
14030 RemovePlayer(player);
14032 if (local_player->friends_still_needed == 0 ||
14033 IS_SP_ELEMENT(Feld[jx][jy]))
14034 PlayerWins(player);
14037 /* this breaks one level: "machine", level 000 */
14039 int move_direction = player->MovDir;
14040 int enter_side = MV_DIR_OPPOSITE(move_direction);
14041 int leave_side = move_direction;
14042 int old_jx = last_jx;
14043 int old_jy = last_jy;
14044 int old_element = Feld[old_jx][old_jy];
14045 int new_element = Feld[jx][jy];
14047 if (IS_CUSTOM_ELEMENT(old_element))
14048 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
14050 player->index_bit, leave_side);
14052 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
14053 CE_PLAYER_LEAVES_X,
14054 player->index_bit, leave_side);
14056 if (IS_CUSTOM_ELEMENT(new_element))
14057 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
14058 player->index_bit, enter_side);
14060 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
14061 CE_PLAYER_ENTERS_X,
14062 player->index_bit, enter_side);
14064 #if USE_FIX_CE_ACTION_WITH_PLAYER
14065 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
14066 CE_MOVE_OF_X, move_direction);
14068 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
14069 CE_MOVE_OF_X, move_direction);
14073 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14075 TestIfPlayerTouchesBadThing(jx, jy);
14076 TestIfPlayerTouchesCustomElement(jx, jy);
14078 /* needed because pushed element has not yet reached its destination,
14079 so it would trigger a change event at its previous field location */
14080 if (!player->is_pushing)
14081 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
14083 if (!player->active)
14084 RemovePlayer(player);
14087 if (!local_player->LevelSolved && level.use_step_counter)
14097 if (TimeLeft <= 10 && setup.time_limit)
14098 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14101 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14103 DisplayGameControlValues();
14105 DrawGameValue_Time(TimeLeft);
14108 if (!TimeLeft && setup.time_limit)
14109 for (i = 0; i < MAX_PLAYERS; i++)
14110 KillPlayer(&stored_player[i]);
14113 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
14115 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
14117 DisplayGameControlValues();
14120 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
14121 DrawGameValue_Time(TimePlayed);
14125 if (tape.single_step && tape.recording && !tape.pausing &&
14126 !player->programmed_action)
14127 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
14131 void ScrollScreen(struct PlayerInfo *player, int mode)
14133 static unsigned int screen_frame_counter = 0;
14135 if (mode == SCROLL_INIT)
14137 /* set scrolling step size according to actual player's moving speed */
14138 ScrollStepSize = TILEX / player->move_delay_value;
14140 screen_frame_counter = FrameCounter;
14141 ScreenMovDir = player->MovDir;
14142 ScreenMovPos = player->MovPos;
14143 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14146 else if (!FrameReached(&screen_frame_counter, 1))
14151 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
14152 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14153 redraw_mask |= REDRAW_FIELD;
14156 ScreenMovDir = MV_NONE;
14159 void TestIfPlayerTouchesCustomElement(int x, int y)
14161 static int xy[4][2] =
14168 static int trigger_sides[4][2] =
14170 /* center side border side */
14171 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14172 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14173 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14174 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14176 static int touch_dir[4] =
14178 MV_LEFT | MV_RIGHT,
14183 int center_element = Feld[x][y]; /* should always be non-moving! */
14186 for (i = 0; i < NUM_DIRECTIONS; i++)
14188 int xx = x + xy[i][0];
14189 int yy = y + xy[i][1];
14190 int center_side = trigger_sides[i][0];
14191 int border_side = trigger_sides[i][1];
14192 int border_element;
14194 if (!IN_LEV_FIELD(xx, yy))
14197 if (IS_PLAYER(x, y)) /* player found at center element */
14199 struct PlayerInfo *player = PLAYERINFO(x, y);
14201 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14202 border_element = Feld[xx][yy]; /* may be moving! */
14203 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14204 border_element = Feld[xx][yy];
14205 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14206 border_element = MovingOrBlocked2Element(xx, yy);
14208 continue; /* center and border element do not touch */
14210 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
14211 player->index_bit, border_side);
14212 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
14213 CE_PLAYER_TOUCHES_X,
14214 player->index_bit, border_side);
14216 #if USE_FIX_CE_ACTION_WITH_PLAYER
14218 /* use player element that is initially defined in the level playfield,
14219 not the player element that corresponds to the runtime player number
14220 (example: a level that contains EL_PLAYER_3 as the only player would
14221 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14222 int player_element = PLAYERINFO(x, y)->initial_element;
14224 CheckElementChangeBySide(xx, yy, border_element, player_element,
14225 CE_TOUCHING_X, border_side);
14229 else if (IS_PLAYER(xx, yy)) /* player found at border element */
14231 struct PlayerInfo *player = PLAYERINFO(xx, yy);
14233 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14235 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14236 continue; /* center and border element do not touch */
14239 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14240 player->index_bit, center_side);
14241 CheckTriggeredElementChangeByPlayer(x, y, center_element,
14242 CE_PLAYER_TOUCHES_X,
14243 player->index_bit, center_side);
14245 #if USE_FIX_CE_ACTION_WITH_PLAYER
14247 /* use player element that is initially defined in the level playfield,
14248 not the player element that corresponds to the runtime player number
14249 (example: a level that contains EL_PLAYER_3 as the only player would
14250 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14251 int player_element = PLAYERINFO(xx, yy)->initial_element;
14253 CheckElementChangeBySide(x, y, center_element, player_element,
14254 CE_TOUCHING_X, center_side);
14263 #if USE_ELEMENT_TOUCHING_BUGFIX
14265 void TestIfElementTouchesCustomElement(int x, int y)
14267 static int xy[4][2] =
14274 static int trigger_sides[4][2] =
14276 /* center side border side */
14277 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14278 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14279 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14280 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14282 static int touch_dir[4] =
14284 MV_LEFT | MV_RIGHT,
14289 boolean change_center_element = FALSE;
14290 int center_element = Feld[x][y]; /* should always be non-moving! */
14291 int border_element_old[NUM_DIRECTIONS];
14294 for (i = 0; i < NUM_DIRECTIONS; i++)
14296 int xx = x + xy[i][0];
14297 int yy = y + xy[i][1];
14298 int border_element;
14300 border_element_old[i] = -1;
14302 if (!IN_LEV_FIELD(xx, yy))
14305 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14306 border_element = Feld[xx][yy]; /* may be moving! */
14307 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14308 border_element = Feld[xx][yy];
14309 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14310 border_element = MovingOrBlocked2Element(xx, yy);
14312 continue; /* center and border element do not touch */
14314 border_element_old[i] = border_element;
14317 for (i = 0; i < NUM_DIRECTIONS; i++)
14319 int xx = x + xy[i][0];
14320 int yy = y + xy[i][1];
14321 int center_side = trigger_sides[i][0];
14322 int border_element = border_element_old[i];
14324 if (border_element == -1)
14327 /* check for change of border element */
14328 CheckElementChangeBySide(xx, yy, border_element, center_element,
14329 CE_TOUCHING_X, center_side);
14331 /* (center element cannot be player, so we dont have to check this here) */
14334 for (i = 0; i < NUM_DIRECTIONS; i++)
14336 int xx = x + xy[i][0];
14337 int yy = y + xy[i][1];
14338 int border_side = trigger_sides[i][1];
14339 int border_element = border_element_old[i];
14341 if (border_element == -1)
14344 /* check for change of center element (but change it only once) */
14345 if (!change_center_element)
14346 change_center_element =
14347 CheckElementChangeBySide(x, y, center_element, border_element,
14348 CE_TOUCHING_X, border_side);
14350 #if USE_FIX_CE_ACTION_WITH_PLAYER
14351 if (IS_PLAYER(xx, yy))
14353 /* use player element that is initially defined in the level playfield,
14354 not the player element that corresponds to the runtime player number
14355 (example: a level that contains EL_PLAYER_3 as the only player would
14356 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14357 int player_element = PLAYERINFO(xx, yy)->initial_element;
14359 CheckElementChangeBySide(x, y, center_element, player_element,
14360 CE_TOUCHING_X, border_side);
14368 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14370 static int xy[4][2] =
14377 static int trigger_sides[4][2] =
14379 /* center side border side */
14380 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14381 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14382 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14383 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14385 static int touch_dir[4] =
14387 MV_LEFT | MV_RIGHT,
14392 boolean change_center_element = FALSE;
14393 int center_element = Feld[x][y]; /* should always be non-moving! */
14396 for (i = 0; i < NUM_DIRECTIONS; i++)
14398 int xx = x + xy[i][0];
14399 int yy = y + xy[i][1];
14400 int center_side = trigger_sides[i][0];
14401 int border_side = trigger_sides[i][1];
14402 int border_element;
14404 if (!IN_LEV_FIELD(xx, yy))
14407 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14408 border_element = Feld[xx][yy]; /* may be moving! */
14409 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14410 border_element = Feld[xx][yy];
14411 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14412 border_element = MovingOrBlocked2Element(xx, yy);
14414 continue; /* center and border element do not touch */
14416 /* check for change of center element (but change it only once) */
14417 if (!change_center_element)
14418 change_center_element =
14419 CheckElementChangeBySide(x, y, center_element, border_element,
14420 CE_TOUCHING_X, border_side);
14422 /* check for change of border element */
14423 CheckElementChangeBySide(xx, yy, border_element, center_element,
14424 CE_TOUCHING_X, center_side);
14430 void TestIfElementHitsCustomElement(int x, int y, int direction)
14432 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14433 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14434 int hitx = x + dx, hity = y + dy;
14435 int hitting_element = Feld[x][y];
14436 int touched_element;
14438 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14441 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14442 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14444 if (IN_LEV_FIELD(hitx, hity))
14446 int opposite_direction = MV_DIR_OPPOSITE(direction);
14447 int hitting_side = direction;
14448 int touched_side = opposite_direction;
14449 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14450 MovDir[hitx][hity] != direction ||
14451 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14457 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14458 CE_HITTING_X, touched_side);
14460 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14461 CE_HIT_BY_X, hitting_side);
14463 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14464 CE_HIT_BY_SOMETHING, opposite_direction);
14466 #if USE_FIX_CE_ACTION_WITH_PLAYER
14467 if (IS_PLAYER(hitx, hity))
14469 /* use player element that is initially defined in the level playfield,
14470 not the player element that corresponds to the runtime player number
14471 (example: a level that contains EL_PLAYER_3 as the only player would
14472 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14473 int player_element = PLAYERINFO(hitx, hity)->initial_element;
14475 CheckElementChangeBySide(x, y, hitting_element, player_element,
14476 CE_HITTING_X, touched_side);
14482 /* "hitting something" is also true when hitting the playfield border */
14483 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14484 CE_HITTING_SOMETHING, direction);
14488 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14490 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14491 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14492 int hitx = x + dx, hity = y + dy;
14493 int hitting_element = Feld[x][y];
14494 int touched_element;
14496 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14497 !IS_FREE(hitx, hity) &&
14498 (!IS_MOVING(hitx, hity) ||
14499 MovDir[hitx][hity] != direction ||
14500 ABS(MovPos[hitx][hity]) <= TILEY / 2));
14503 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14507 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14511 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14512 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14514 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14515 EP_CAN_SMASH_EVERYTHING, direction);
14517 if (IN_LEV_FIELD(hitx, hity))
14519 int opposite_direction = MV_DIR_OPPOSITE(direction);
14520 int hitting_side = direction;
14521 int touched_side = opposite_direction;
14523 int touched_element = MovingOrBlocked2Element(hitx, hity);
14526 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14527 MovDir[hitx][hity] != direction ||
14528 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14537 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14538 CE_SMASHED_BY_SOMETHING, opposite_direction);
14540 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14541 CE_OTHER_IS_SMASHING, touched_side);
14543 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14544 CE_OTHER_GETS_SMASHED, hitting_side);
14550 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14552 int i, kill_x = -1, kill_y = -1;
14554 int bad_element = -1;
14555 static int test_xy[4][2] =
14562 static int test_dir[4] =
14570 for (i = 0; i < NUM_DIRECTIONS; i++)
14572 int test_x, test_y, test_move_dir, test_element;
14574 test_x = good_x + test_xy[i][0];
14575 test_y = good_y + test_xy[i][1];
14577 if (!IN_LEV_FIELD(test_x, test_y))
14581 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14583 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14585 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14586 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14588 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14589 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
14593 bad_element = test_element;
14599 if (kill_x != -1 || kill_y != -1)
14601 if (IS_PLAYER(good_x, good_y))
14603 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14605 if (player->shield_deadly_time_left > 0 &&
14606 !IS_INDESTRUCTIBLE(bad_element))
14607 Bang(kill_x, kill_y);
14608 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14609 KillPlayer(player);
14612 Bang(good_x, good_y);
14616 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14618 int i, kill_x = -1, kill_y = -1;
14619 int bad_element = Feld[bad_x][bad_y];
14620 static int test_xy[4][2] =
14627 static int touch_dir[4] =
14629 MV_LEFT | MV_RIGHT,
14634 static int test_dir[4] =
14642 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
14645 for (i = 0; i < NUM_DIRECTIONS; i++)
14647 int test_x, test_y, test_move_dir, test_element;
14649 test_x = bad_x + test_xy[i][0];
14650 test_y = bad_y + test_xy[i][1];
14652 if (!IN_LEV_FIELD(test_x, test_y))
14656 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14658 test_element = Feld[test_x][test_y];
14660 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14661 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14663 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
14664 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
14666 /* good thing is player or penguin that does not move away */
14667 if (IS_PLAYER(test_x, test_y))
14669 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14671 if (bad_element == EL_ROBOT && player->is_moving)
14672 continue; /* robot does not kill player if he is moving */
14674 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14676 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14677 continue; /* center and border element do not touch */
14685 else if (test_element == EL_PENGUIN)
14695 if (kill_x != -1 || kill_y != -1)
14697 if (IS_PLAYER(kill_x, kill_y))
14699 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14701 if (player->shield_deadly_time_left > 0 &&
14702 !IS_INDESTRUCTIBLE(bad_element))
14703 Bang(bad_x, bad_y);
14704 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14705 KillPlayer(player);
14708 Bang(kill_x, kill_y);
14712 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14714 int bad_element = Feld[bad_x][bad_y];
14715 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14716 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
14717 int test_x = bad_x + dx, test_y = bad_y + dy;
14718 int test_move_dir, test_element;
14719 int kill_x = -1, kill_y = -1;
14721 if (!IN_LEV_FIELD(test_x, test_y))
14725 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14727 test_element = Feld[test_x][test_y];
14729 if (test_move_dir != bad_move_dir)
14731 /* good thing can be player or penguin that does not move away */
14732 if (IS_PLAYER(test_x, test_y))
14734 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14736 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14737 player as being hit when he is moving towards the bad thing, because
14738 the "get hit by" condition would be lost after the player stops) */
14739 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14740 return; /* player moves away from bad thing */
14745 else if (test_element == EL_PENGUIN)
14752 if (kill_x != -1 || kill_y != -1)
14754 if (IS_PLAYER(kill_x, kill_y))
14756 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14758 if (player->shield_deadly_time_left > 0 &&
14759 !IS_INDESTRUCTIBLE(bad_element))
14760 Bang(bad_x, bad_y);
14761 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14762 KillPlayer(player);
14765 Bang(kill_x, kill_y);
14769 void TestIfPlayerTouchesBadThing(int x, int y)
14771 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14774 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14776 TestIfGoodThingHitsBadThing(x, y, move_dir);
14779 void TestIfBadThingTouchesPlayer(int x, int y)
14781 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14784 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14786 TestIfBadThingHitsGoodThing(x, y, move_dir);
14789 void TestIfFriendTouchesBadThing(int x, int y)
14791 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14794 void TestIfBadThingTouchesFriend(int x, int y)
14796 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14799 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14801 int i, kill_x = bad_x, kill_y = bad_y;
14802 static int xy[4][2] =
14810 for (i = 0; i < NUM_DIRECTIONS; i++)
14814 x = bad_x + xy[i][0];
14815 y = bad_y + xy[i][1];
14816 if (!IN_LEV_FIELD(x, y))
14819 element = Feld[x][y];
14820 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14821 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14829 if (kill_x != bad_x || kill_y != bad_y)
14830 Bang(bad_x, bad_y);
14833 void KillPlayer(struct PlayerInfo *player)
14835 int jx = player->jx, jy = player->jy;
14837 if (!player->active)
14841 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14842 player->killed, player->active, player->reanimated);
14845 /* the following code was introduced to prevent an infinite loop when calling
14847 -> CheckTriggeredElementChangeExt()
14848 -> ExecuteCustomElementAction()
14850 -> (infinitely repeating the above sequence of function calls)
14851 which occurs when killing the player while having a CE with the setting
14852 "kill player X when explosion of <player X>"; the solution using a new
14853 field "player->killed" was chosen for backwards compatibility, although
14854 clever use of the fields "player->active" etc. would probably also work */
14856 if (player->killed)
14860 player->killed = TRUE;
14862 /* remove accessible field at the player's position */
14863 Feld[jx][jy] = EL_EMPTY;
14865 /* deactivate shield (else Bang()/Explode() would not work right) */
14866 player->shield_normal_time_left = 0;
14867 player->shield_deadly_time_left = 0;
14870 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14871 player->killed, player->active, player->reanimated);
14877 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14878 player->killed, player->active, player->reanimated);
14881 #if USE_PLAYER_REANIMATION
14883 if (player->reanimated) /* killed player may have been reanimated */
14884 player->killed = player->reanimated = FALSE;
14886 BuryPlayer(player);
14888 if (player->killed) /* player may have been reanimated */
14889 BuryPlayer(player);
14892 BuryPlayer(player);
14896 static void KillPlayerUnlessEnemyProtected(int x, int y)
14898 if (!PLAYER_ENEMY_PROTECTED(x, y))
14899 KillPlayer(PLAYERINFO(x, y));
14902 static void KillPlayerUnlessExplosionProtected(int x, int y)
14904 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14905 KillPlayer(PLAYERINFO(x, y));
14908 void BuryPlayer(struct PlayerInfo *player)
14910 int jx = player->jx, jy = player->jy;
14912 if (!player->active)
14915 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14916 PlayLevelSound(jx, jy, SND_GAME_LOSING);
14918 player->GameOver = TRUE;
14919 RemovePlayer(player);
14922 void RemovePlayer(struct PlayerInfo *player)
14924 int jx = player->jx, jy = player->jy;
14925 int i, found = FALSE;
14927 player->present = FALSE;
14928 player->active = FALSE;
14930 if (!ExplodeField[jx][jy])
14931 StorePlayer[jx][jy] = 0;
14933 if (player->is_moving)
14934 TEST_DrawLevelField(player->last_jx, player->last_jy);
14936 for (i = 0; i < MAX_PLAYERS; i++)
14937 if (stored_player[i].active)
14941 AllPlayersGone = TRUE;
14947 #if USE_NEW_SNAP_DELAY
14948 static void setFieldForSnapping(int x, int y, int element, int direction)
14950 struct ElementInfo *ei = &element_info[element];
14951 int direction_bit = MV_DIR_TO_BIT(direction);
14952 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14953 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14954 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14956 Feld[x][y] = EL_ELEMENT_SNAPPING;
14957 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14959 ResetGfxAnimation(x, y);
14961 GfxElement[x][y] = element;
14962 GfxAction[x][y] = action;
14963 GfxDir[x][y] = direction;
14964 GfxFrame[x][y] = -1;
14969 =============================================================================
14970 checkDiagonalPushing()
14971 -----------------------------------------------------------------------------
14972 check if diagonal input device direction results in pushing of object
14973 (by checking if the alternative direction is walkable, diggable, ...)
14974 =============================================================================
14977 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14978 int x, int y, int real_dx, int real_dy)
14980 int jx, jy, dx, dy, xx, yy;
14982 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
14985 /* diagonal direction: check alternative direction */
14990 xx = jx + (dx == 0 ? real_dx : 0);
14991 yy = jy + (dy == 0 ? real_dy : 0);
14993 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14997 =============================================================================
14999 -----------------------------------------------------------------------------
15000 x, y: field next to player (non-diagonal) to try to dig to
15001 real_dx, real_dy: direction as read from input device (can be diagonal)
15002 =============================================================================
15005 static int DigField(struct PlayerInfo *player,
15006 int oldx, int oldy, int x, int y,
15007 int real_dx, int real_dy, int mode)
15009 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
15010 boolean player_was_pushing = player->is_pushing;
15011 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
15012 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
15013 int jx = oldx, jy = oldy;
15014 int dx = x - jx, dy = y - jy;
15015 int nextx = x + dx, nexty = y + dy;
15016 int move_direction = (dx == -1 ? MV_LEFT :
15017 dx == +1 ? MV_RIGHT :
15019 dy == +1 ? MV_DOWN : MV_NONE);
15020 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
15021 int dig_side = MV_DIR_OPPOSITE(move_direction);
15022 int old_element = Feld[jx][jy];
15023 #if USE_FIXED_DONT_RUN_INTO
15024 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
15030 if (is_player) /* function can also be called by EL_PENGUIN */
15032 if (player->MovPos == 0)
15034 player->is_digging = FALSE;
15035 player->is_collecting = FALSE;
15038 if (player->MovPos == 0) /* last pushing move finished */
15039 player->is_pushing = FALSE;
15041 if (mode == DF_NO_PUSH) /* player just stopped pushing */
15043 player->is_switching = FALSE;
15044 player->push_delay = -1;
15046 return MP_NO_ACTION;
15050 #if !USE_FIXED_DONT_RUN_INTO
15051 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
15052 return MP_NO_ACTION;
15055 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
15056 old_element = Back[jx][jy];
15058 /* in case of element dropped at player position, check background */
15059 else if (Back[jx][jy] != EL_EMPTY &&
15060 game.engine_version >= VERSION_IDENT(2,2,0,0))
15061 old_element = Back[jx][jy];
15063 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
15064 return MP_NO_ACTION; /* field has no opening in this direction */
15066 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
15067 return MP_NO_ACTION; /* field has no opening in this direction */
15069 #if USE_FIXED_DONT_RUN_INTO
15070 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
15074 Feld[jx][jy] = player->artwork_element;
15075 InitMovingField(jx, jy, MV_DOWN);
15076 Store[jx][jy] = EL_ACID;
15077 ContinueMoving(jx, jy);
15078 BuryPlayer(player);
15080 return MP_DONT_RUN_INTO;
15084 #if USE_FIXED_DONT_RUN_INTO
15085 if (player_can_move && DONT_RUN_INTO(element))
15087 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
15089 return MP_DONT_RUN_INTO;
15093 #if USE_FIXED_DONT_RUN_INTO
15094 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
15095 return MP_NO_ACTION;
15098 #if !USE_FIXED_DONT_RUN_INTO
15099 element = Feld[x][y];
15102 collect_count = element_info[element].collect_count_initial;
15104 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
15105 return MP_NO_ACTION;
15107 if (game.engine_version < VERSION_IDENT(2,2,0,0))
15108 player_can_move = player_can_move_or_snap;
15110 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
15111 game.engine_version >= VERSION_IDENT(2,2,0,0))
15113 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
15114 player->index_bit, dig_side);
15115 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15116 player->index_bit, dig_side);
15118 if (element == EL_DC_LANDMINE)
15121 if (Feld[x][y] != element) /* field changed by snapping */
15124 return MP_NO_ACTION;
15127 #if USE_PLAYER_GRAVITY
15128 if (player->gravity && is_player && !player->is_auto_moving &&
15129 canFallDown(player) && move_direction != MV_DOWN &&
15130 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15131 return MP_NO_ACTION; /* player cannot walk here due to gravity */
15133 if (game.gravity && is_player && !player->is_auto_moving &&
15134 canFallDown(player) && move_direction != MV_DOWN &&
15135 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15136 return MP_NO_ACTION; /* player cannot walk here due to gravity */
15139 if (player_can_move &&
15140 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
15142 int sound_element = SND_ELEMENT(element);
15143 int sound_action = ACTION_WALKING;
15145 if (IS_RND_GATE(element))
15147 if (!player->key[RND_GATE_NR(element)])
15148 return MP_NO_ACTION;
15150 else if (IS_RND_GATE_GRAY(element))
15152 if (!player->key[RND_GATE_GRAY_NR(element)])
15153 return MP_NO_ACTION;
15155 else if (IS_RND_GATE_GRAY_ACTIVE(element))
15157 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
15158 return MP_NO_ACTION;
15160 else if (element == EL_EXIT_OPEN ||
15161 element == EL_EM_EXIT_OPEN ||
15163 element == EL_EM_EXIT_OPENING ||
15165 element == EL_STEEL_EXIT_OPEN ||
15166 element == EL_EM_STEEL_EXIT_OPEN ||
15168 element == EL_EM_STEEL_EXIT_OPENING ||
15170 element == EL_SP_EXIT_OPEN ||
15171 element == EL_SP_EXIT_OPENING)
15173 sound_action = ACTION_PASSING; /* player is passing exit */
15175 else if (element == EL_EMPTY)
15177 sound_action = ACTION_MOVING; /* nothing to walk on */
15180 /* play sound from background or player, whatever is available */
15181 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
15182 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
15184 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
15186 else if (player_can_move &&
15187 IS_PASSABLE(element) && canPassField(x, y, move_direction))
15189 if (!ACCESS_FROM(element, opposite_direction))
15190 return MP_NO_ACTION; /* field not accessible from this direction */
15192 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
15193 return MP_NO_ACTION;
15195 if (IS_EM_GATE(element))
15197 if (!player->key[EM_GATE_NR(element)])
15198 return MP_NO_ACTION;
15200 else if (IS_EM_GATE_GRAY(element))
15202 if (!player->key[EM_GATE_GRAY_NR(element)])
15203 return MP_NO_ACTION;
15205 else if (IS_EM_GATE_GRAY_ACTIVE(element))
15207 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
15208 return MP_NO_ACTION;
15210 else if (IS_EMC_GATE(element))
15212 if (!player->key[EMC_GATE_NR(element)])
15213 return MP_NO_ACTION;
15215 else if (IS_EMC_GATE_GRAY(element))
15217 if (!player->key[EMC_GATE_GRAY_NR(element)])
15218 return MP_NO_ACTION;
15220 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
15222 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
15223 return MP_NO_ACTION;
15225 else if (element == EL_DC_GATE_WHITE ||
15226 element == EL_DC_GATE_WHITE_GRAY ||
15227 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15229 if (player->num_white_keys == 0)
15230 return MP_NO_ACTION;
15232 player->num_white_keys--;
15234 else if (IS_SP_PORT(element))
15236 if (element == EL_SP_GRAVITY_PORT_LEFT ||
15237 element == EL_SP_GRAVITY_PORT_RIGHT ||
15238 element == EL_SP_GRAVITY_PORT_UP ||
15239 element == EL_SP_GRAVITY_PORT_DOWN)
15240 #if USE_PLAYER_GRAVITY
15241 player->gravity = !player->gravity;
15243 game.gravity = !game.gravity;
15245 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15246 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15247 element == EL_SP_GRAVITY_ON_PORT_UP ||
15248 element == EL_SP_GRAVITY_ON_PORT_DOWN)
15249 #if USE_PLAYER_GRAVITY
15250 player->gravity = TRUE;
15252 game.gravity = TRUE;
15254 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15255 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15256 element == EL_SP_GRAVITY_OFF_PORT_UP ||
15257 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15258 #if USE_PLAYER_GRAVITY
15259 player->gravity = FALSE;
15261 game.gravity = FALSE;
15265 /* automatically move to the next field with double speed */
15266 player->programmed_action = move_direction;
15268 if (player->move_delay_reset_counter == 0)
15270 player->move_delay_reset_counter = 2; /* two double speed steps */
15272 DOUBLE_PLAYER_SPEED(player);
15275 PlayLevelSoundAction(x, y, ACTION_PASSING);
15277 else if (player_can_move_or_snap && IS_DIGGABLE(element))
15281 if (mode != DF_SNAP)
15283 GfxElement[x][y] = GFX_ELEMENT(element);
15284 player->is_digging = TRUE;
15287 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15289 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15290 player->index_bit, dig_side);
15292 if (mode == DF_SNAP)
15294 #if USE_NEW_SNAP_DELAY
15295 if (level.block_snap_field)
15296 setFieldForSnapping(x, y, element, move_direction);
15298 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15300 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15303 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15304 player->index_bit, dig_side);
15307 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15311 if (is_player && mode != DF_SNAP)
15313 GfxElement[x][y] = element;
15314 player->is_collecting = TRUE;
15317 if (element == EL_SPEED_PILL)
15319 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15321 else if (element == EL_EXTRA_TIME && level.time > 0)
15323 TimeLeft += level.extra_time;
15326 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15328 DisplayGameControlValues();
15330 DrawGameValue_Time(TimeLeft);
15333 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15335 player->shield_normal_time_left += level.shield_normal_time;
15336 if (element == EL_SHIELD_DEADLY)
15337 player->shield_deadly_time_left += level.shield_deadly_time;
15339 else if (element == EL_DYNAMITE ||
15340 element == EL_EM_DYNAMITE ||
15341 element == EL_SP_DISK_RED)
15343 if (player->inventory_size < MAX_INVENTORY_SIZE)
15344 player->inventory_element[player->inventory_size++] = element;
15346 DrawGameDoorValues();
15348 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15350 player->dynabomb_count++;
15351 player->dynabombs_left++;
15353 else if (element == EL_DYNABOMB_INCREASE_SIZE)
15355 player->dynabomb_size++;
15357 else if (element == EL_DYNABOMB_INCREASE_POWER)
15359 player->dynabomb_xl = TRUE;
15361 else if (IS_KEY(element))
15363 player->key[KEY_NR(element)] = TRUE;
15365 DrawGameDoorValues();
15367 else if (element == EL_DC_KEY_WHITE)
15369 player->num_white_keys++;
15371 /* display white keys? */
15372 /* DrawGameDoorValues(); */
15374 else if (IS_ENVELOPE(element))
15376 player->show_envelope = element;
15378 else if (element == EL_EMC_LENSES)
15380 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15382 RedrawAllInvisibleElementsForLenses();
15384 else if (element == EL_EMC_MAGNIFIER)
15386 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15388 RedrawAllInvisibleElementsForMagnifier();
15390 else if (IS_DROPPABLE(element) ||
15391 IS_THROWABLE(element)) /* can be collected and dropped */
15395 if (collect_count == 0)
15396 player->inventory_infinite_element = element;
15398 for (i = 0; i < collect_count; i++)
15399 if (player->inventory_size < MAX_INVENTORY_SIZE)
15400 player->inventory_element[player->inventory_size++] = element;
15402 DrawGameDoorValues();
15404 else if (collect_count > 0)
15406 local_player->gems_still_needed -= collect_count;
15407 if (local_player->gems_still_needed < 0)
15408 local_player->gems_still_needed = 0;
15411 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15413 DisplayGameControlValues();
15415 DrawGameValue_Emeralds(local_player->gems_still_needed);
15419 RaiseScoreElement(element);
15420 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15423 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15424 player->index_bit, dig_side);
15426 if (mode == DF_SNAP)
15428 #if USE_NEW_SNAP_DELAY
15429 if (level.block_snap_field)
15430 setFieldForSnapping(x, y, element, move_direction);
15432 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15434 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15437 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15438 player->index_bit, dig_side);
15441 else if (player_can_move_or_snap && IS_PUSHABLE(element))
15443 if (mode == DF_SNAP && element != EL_BD_ROCK)
15444 return MP_NO_ACTION;
15446 if (CAN_FALL(element) && dy)
15447 return MP_NO_ACTION;
15449 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15450 !(element == EL_SPRING && level.use_spring_bug))
15451 return MP_NO_ACTION;
15453 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15454 ((move_direction & MV_VERTICAL &&
15455 ((element_info[element].move_pattern & MV_LEFT &&
15456 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15457 (element_info[element].move_pattern & MV_RIGHT &&
15458 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15459 (move_direction & MV_HORIZONTAL &&
15460 ((element_info[element].move_pattern & MV_UP &&
15461 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15462 (element_info[element].move_pattern & MV_DOWN &&
15463 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15464 return MP_NO_ACTION;
15466 /* do not push elements already moving away faster than player */
15467 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15468 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15469 return MP_NO_ACTION;
15471 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15473 if (player->push_delay_value == -1 || !player_was_pushing)
15474 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15476 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15478 if (player->push_delay_value == -1)
15479 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15481 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15483 if (!player->is_pushing)
15484 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15487 player->is_pushing = TRUE;
15488 player->is_active = TRUE;
15490 if (!(IN_LEV_FIELD(nextx, nexty) &&
15491 (IS_FREE(nextx, nexty) ||
15492 (IS_SB_ELEMENT(element) &&
15493 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15494 (IS_CUSTOM_ELEMENT(element) &&
15495 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15496 return MP_NO_ACTION;
15498 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15499 return MP_NO_ACTION;
15501 if (player->push_delay == -1) /* new pushing; restart delay */
15502 player->push_delay = 0;
15504 if (player->push_delay < player->push_delay_value &&
15505 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15506 element != EL_SPRING && element != EL_BALLOON)
15508 /* make sure that there is no move delay before next try to push */
15509 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15510 player->move_delay = 0;
15512 return MP_NO_ACTION;
15515 if (IS_CUSTOM_ELEMENT(element) &&
15516 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15518 if (!DigFieldByCE(nextx, nexty, element))
15519 return MP_NO_ACTION;
15522 if (IS_SB_ELEMENT(element))
15524 if (element == EL_SOKOBAN_FIELD_FULL)
15526 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15527 local_player->sokobanfields_still_needed++;
15530 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15532 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15533 local_player->sokobanfields_still_needed--;
15536 Feld[x][y] = EL_SOKOBAN_OBJECT;
15538 if (Back[x][y] == Back[nextx][nexty])
15539 PlayLevelSoundAction(x, y, ACTION_PUSHING);
15540 else if (Back[x][y] != 0)
15541 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15544 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15548 if (local_player->sokobanfields_still_needed == 0 &&
15549 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
15551 if (local_player->sokobanfields_still_needed == 0 &&
15552 game.emulation == EMU_SOKOBAN)
15555 PlayerWins(player);
15557 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15561 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15563 InitMovingField(x, y, move_direction);
15564 GfxAction[x][y] = ACTION_PUSHING;
15566 if (mode == DF_SNAP)
15567 ContinueMoving(x, y);
15569 MovPos[x][y] = (dx != 0 ? dx : dy);
15571 Pushed[x][y] = TRUE;
15572 Pushed[nextx][nexty] = TRUE;
15574 if (game.engine_version < VERSION_IDENT(2,2,0,7))
15575 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15577 player->push_delay_value = -1; /* get new value later */
15579 /* check for element change _after_ element has been pushed */
15580 if (game.use_change_when_pushing_bug)
15582 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15583 player->index_bit, dig_side);
15584 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15585 player->index_bit, dig_side);
15588 else if (IS_SWITCHABLE(element))
15590 if (PLAYER_SWITCHING(player, x, y))
15592 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15593 player->index_bit, dig_side);
15598 player->is_switching = TRUE;
15599 player->switch_x = x;
15600 player->switch_y = y;
15602 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15604 if (element == EL_ROBOT_WHEEL)
15606 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15610 game.robot_wheel_active = TRUE;
15612 TEST_DrawLevelField(x, y);
15614 else if (element == EL_SP_TERMINAL)
15618 SCAN_PLAYFIELD(xx, yy)
15620 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15622 else if (Feld[xx][yy] == EL_SP_TERMINAL)
15623 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15626 else if (IS_BELT_SWITCH(element))
15628 ToggleBeltSwitch(x, y);
15630 else if (element == EL_SWITCHGATE_SWITCH_UP ||
15631 element == EL_SWITCHGATE_SWITCH_DOWN ||
15632 element == EL_DC_SWITCHGATE_SWITCH_UP ||
15633 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15635 ToggleSwitchgateSwitch(x, y);
15637 else if (element == EL_LIGHT_SWITCH ||
15638 element == EL_LIGHT_SWITCH_ACTIVE)
15640 ToggleLightSwitch(x, y);
15642 else if (element == EL_TIMEGATE_SWITCH ||
15643 element == EL_DC_TIMEGATE_SWITCH)
15645 ActivateTimegateSwitch(x, y);
15647 else if (element == EL_BALLOON_SWITCH_LEFT ||
15648 element == EL_BALLOON_SWITCH_RIGHT ||
15649 element == EL_BALLOON_SWITCH_UP ||
15650 element == EL_BALLOON_SWITCH_DOWN ||
15651 element == EL_BALLOON_SWITCH_NONE ||
15652 element == EL_BALLOON_SWITCH_ANY)
15654 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
15655 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15656 element == EL_BALLOON_SWITCH_UP ? MV_UP :
15657 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
15658 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
15661 else if (element == EL_LAMP)
15663 Feld[x][y] = EL_LAMP_ACTIVE;
15664 local_player->lights_still_needed--;
15666 ResetGfxAnimation(x, y);
15667 TEST_DrawLevelField(x, y);
15669 else if (element == EL_TIME_ORB_FULL)
15671 Feld[x][y] = EL_TIME_ORB_EMPTY;
15673 if (level.time > 0 || level.use_time_orb_bug)
15675 TimeLeft += level.time_orb_time;
15676 game.no_time_limit = FALSE;
15679 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15681 DisplayGameControlValues();
15683 DrawGameValue_Time(TimeLeft);
15687 ResetGfxAnimation(x, y);
15688 TEST_DrawLevelField(x, y);
15690 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15691 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15695 game.ball_state = !game.ball_state;
15697 SCAN_PLAYFIELD(xx, yy)
15699 int e = Feld[xx][yy];
15701 if (game.ball_state)
15703 if (e == EL_EMC_MAGIC_BALL)
15704 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15705 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15706 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15710 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15711 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15712 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15713 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15718 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15719 player->index_bit, dig_side);
15721 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15722 player->index_bit, dig_side);
15724 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15725 player->index_bit, dig_side);
15731 if (!PLAYER_SWITCHING(player, x, y))
15733 player->is_switching = TRUE;
15734 player->switch_x = x;
15735 player->switch_y = y;
15737 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15738 player->index_bit, dig_side);
15739 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15740 player->index_bit, dig_side);
15742 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15743 player->index_bit, dig_side);
15744 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15745 player->index_bit, dig_side);
15748 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15749 player->index_bit, dig_side);
15750 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15751 player->index_bit, dig_side);
15753 return MP_NO_ACTION;
15756 player->push_delay = -1;
15758 if (is_player) /* function can also be called by EL_PENGUIN */
15760 if (Feld[x][y] != element) /* really digged/collected something */
15762 player->is_collecting = !player->is_digging;
15763 player->is_active = TRUE;
15770 static boolean DigFieldByCE(int x, int y, int digging_element)
15772 int element = Feld[x][y];
15774 if (!IS_FREE(x, y))
15776 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15777 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15780 /* no element can dig solid indestructible elements */
15781 if (IS_INDESTRUCTIBLE(element) &&
15782 !IS_DIGGABLE(element) &&
15783 !IS_COLLECTIBLE(element))
15786 if (AmoebaNr[x][y] &&
15787 (element == EL_AMOEBA_FULL ||
15788 element == EL_BD_AMOEBA ||
15789 element == EL_AMOEBA_GROWING))
15791 AmoebaCnt[AmoebaNr[x][y]]--;
15792 AmoebaCnt2[AmoebaNr[x][y]]--;
15795 if (IS_MOVING(x, y))
15796 RemoveMovingField(x, y);
15800 TEST_DrawLevelField(x, y);
15803 /* if digged element was about to explode, prevent the explosion */
15804 ExplodeField[x][y] = EX_TYPE_NONE;
15806 PlayLevelSoundAction(x, y, action);
15809 Store[x][y] = EL_EMPTY;
15812 /* this makes it possible to leave the removed element again */
15813 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15814 Store[x][y] = element;
15816 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15818 int move_leave_element = element_info[digging_element].move_leave_element;
15820 /* this makes it possible to leave the removed element again */
15821 Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15822 element : move_leave_element);
15829 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15831 int jx = player->jx, jy = player->jy;
15832 int x = jx + dx, y = jy + dy;
15833 int snap_direction = (dx == -1 ? MV_LEFT :
15834 dx == +1 ? MV_RIGHT :
15836 dy == +1 ? MV_DOWN : MV_NONE);
15837 boolean can_continue_snapping = (level.continuous_snapping &&
15838 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15840 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15843 if (!player->active || !IN_LEV_FIELD(x, y))
15851 if (player->MovPos == 0)
15852 player->is_pushing = FALSE;
15854 player->is_snapping = FALSE;
15856 if (player->MovPos == 0)
15858 player->is_moving = FALSE;
15859 player->is_digging = FALSE;
15860 player->is_collecting = FALSE;
15866 #if USE_NEW_CONTINUOUS_SNAPPING
15867 /* prevent snapping with already pressed snap key when not allowed */
15868 if (player->is_snapping && !can_continue_snapping)
15871 if (player->is_snapping)
15875 player->MovDir = snap_direction;
15877 if (player->MovPos == 0)
15879 player->is_moving = FALSE;
15880 player->is_digging = FALSE;
15881 player->is_collecting = FALSE;
15884 player->is_dropping = FALSE;
15885 player->is_dropping_pressed = FALSE;
15886 player->drop_pressed_delay = 0;
15888 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15891 player->is_snapping = TRUE;
15892 player->is_active = TRUE;
15894 if (player->MovPos == 0)
15896 player->is_moving = FALSE;
15897 player->is_digging = FALSE;
15898 player->is_collecting = FALSE;
15901 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
15902 TEST_DrawLevelField(player->last_jx, player->last_jy);
15904 TEST_DrawLevelField(x, y);
15909 static boolean DropElement(struct PlayerInfo *player)
15911 int old_element, new_element;
15912 int dropx = player->jx, dropy = player->jy;
15913 int drop_direction = player->MovDir;
15914 int drop_side = drop_direction;
15916 int drop_element = get_next_dropped_element(player);
15918 int drop_element = (player->inventory_size > 0 ?
15919 player->inventory_element[player->inventory_size - 1] :
15920 player->inventory_infinite_element != EL_UNDEFINED ?
15921 player->inventory_infinite_element :
15922 player->dynabombs_left > 0 ?
15923 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15927 player->is_dropping_pressed = TRUE;
15929 /* do not drop an element on top of another element; when holding drop key
15930 pressed without moving, dropped element must move away before the next
15931 element can be dropped (this is especially important if the next element
15932 is dynamite, which can be placed on background for historical reasons) */
15933 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15936 if (IS_THROWABLE(drop_element))
15938 dropx += GET_DX_FROM_DIR(drop_direction);
15939 dropy += GET_DY_FROM_DIR(drop_direction);
15941 if (!IN_LEV_FIELD(dropx, dropy))
15945 old_element = Feld[dropx][dropy]; /* old element at dropping position */
15946 new_element = drop_element; /* default: no change when dropping */
15948 /* check if player is active, not moving and ready to drop */
15949 if (!player->active || player->MovPos || player->drop_delay > 0)
15952 /* check if player has anything that can be dropped */
15953 if (new_element == EL_UNDEFINED)
15956 /* check if drop key was pressed long enough for EM style dynamite */
15957 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15960 /* check if anything can be dropped at the current position */
15961 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15964 /* collected custom elements can only be dropped on empty fields */
15965 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15968 if (old_element != EL_EMPTY)
15969 Back[dropx][dropy] = old_element; /* store old element on this field */
15971 ResetGfxAnimation(dropx, dropy);
15972 ResetRandomAnimationValue(dropx, dropy);
15974 if (player->inventory_size > 0 ||
15975 player->inventory_infinite_element != EL_UNDEFINED)
15977 if (player->inventory_size > 0)
15979 player->inventory_size--;
15981 DrawGameDoorValues();
15983 if (new_element == EL_DYNAMITE)
15984 new_element = EL_DYNAMITE_ACTIVE;
15985 else if (new_element == EL_EM_DYNAMITE)
15986 new_element = EL_EM_DYNAMITE_ACTIVE;
15987 else if (new_element == EL_SP_DISK_RED)
15988 new_element = EL_SP_DISK_RED_ACTIVE;
15991 Feld[dropx][dropy] = new_element;
15993 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15994 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15995 el2img(Feld[dropx][dropy]), 0);
15997 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15999 /* needed if previous element just changed to "empty" in the last frame */
16000 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
16002 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
16003 player->index_bit, drop_side);
16004 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
16006 player->index_bit, drop_side);
16008 TestIfElementTouchesCustomElement(dropx, dropy);
16010 else /* player is dropping a dyna bomb */
16012 player->dynabombs_left--;
16014 Feld[dropx][dropy] = new_element;
16016 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
16017 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
16018 el2img(Feld[dropx][dropy]), 0);
16020 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
16023 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
16024 InitField_WithBug1(dropx, dropy, FALSE);
16026 new_element = Feld[dropx][dropy]; /* element might have changed */
16028 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
16029 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
16031 int move_direction, nextx, nexty;
16033 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
16034 MovDir[dropx][dropy] = drop_direction;
16036 move_direction = MovDir[dropx][dropy];
16037 nextx = dropx + GET_DX_FROM_DIR(move_direction);
16038 nexty = dropy + GET_DY_FROM_DIR(move_direction);
16040 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
16042 #if USE_FIX_IMPACT_COLLISION
16043 /* do not cause impact style collision by dropping elements that can fall */
16044 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
16046 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
16050 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
16051 player->is_dropping = TRUE;
16053 player->drop_pressed_delay = 0;
16054 player->is_dropping_pressed = FALSE;
16056 player->drop_x = dropx;
16057 player->drop_y = dropy;
16062 /* ------------------------------------------------------------------------- */
16063 /* game sound playing functions */
16064 /* ------------------------------------------------------------------------- */
16066 static int *loop_sound_frame = NULL;
16067 static int *loop_sound_volume = NULL;
16069 void InitPlayLevelSound()
16071 int num_sounds = getSoundListSize();
16073 checked_free(loop_sound_frame);
16074 checked_free(loop_sound_volume);
16076 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
16077 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
16080 static void PlayLevelSound(int x, int y, int nr)
16082 int sx = SCREENX(x), sy = SCREENY(y);
16083 int volume, stereo_position;
16084 int max_distance = 8;
16085 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
16087 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
16088 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
16091 if (!IN_LEV_FIELD(x, y) ||
16092 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
16093 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
16096 volume = SOUND_MAX_VOLUME;
16098 if (!IN_SCR_FIELD(sx, sy))
16100 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
16101 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
16103 volume -= volume * (dx > dy ? dx : dy) / max_distance;
16106 stereo_position = (SOUND_MAX_LEFT +
16107 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
16108 (SCR_FIELDX + 2 * max_distance));
16110 if (IS_LOOP_SOUND(nr))
16112 /* This assures that quieter loop sounds do not overwrite louder ones,
16113 while restarting sound volume comparison with each new game frame. */
16115 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
16118 loop_sound_volume[nr] = volume;
16119 loop_sound_frame[nr] = FrameCounter;
16122 PlaySoundExt(nr, volume, stereo_position, type);
16125 static void PlayLevelSoundNearest(int x, int y, int sound_action)
16127 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
16128 x > LEVELX(BX2) ? LEVELX(BX2) : x,
16129 y < LEVELY(BY1) ? LEVELY(BY1) :
16130 y > LEVELY(BY2) ? LEVELY(BY2) : y,
16134 static void PlayLevelSoundAction(int x, int y, int action)
16136 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
16139 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
16141 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16143 if (sound_effect != SND_UNDEFINED)
16144 PlayLevelSound(x, y, sound_effect);
16147 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
16150 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16152 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16153 PlayLevelSound(x, y, sound_effect);
16156 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
16158 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16160 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16161 PlayLevelSound(x, y, sound_effect);
16164 static void StopLevelSoundActionIfLoop(int x, int y, int action)
16166 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16168 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16169 StopSound(sound_effect);
16172 static void PlayLevelMusic()
16174 if (levelset.music[level_nr] != MUS_UNDEFINED)
16175 PlayMusic(levelset.music[level_nr]); /* from config file */
16177 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
16180 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
16182 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
16183 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
16184 int x = xx - 1 - offset;
16185 int y = yy - 1 - offset;
16190 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
16194 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16198 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16202 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16206 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16210 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16214 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16217 case SAMPLE_android_clone:
16218 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16221 case SAMPLE_android_move:
16222 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16225 case SAMPLE_spring:
16226 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16230 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16234 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16237 case SAMPLE_eater_eat:
16238 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16242 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16245 case SAMPLE_collect:
16246 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16249 case SAMPLE_diamond:
16250 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16253 case SAMPLE_squash:
16254 /* !!! CHECK THIS !!! */
16256 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16258 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16262 case SAMPLE_wonderfall:
16263 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16267 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16271 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16275 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16279 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16283 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16287 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16290 case SAMPLE_wonder:
16291 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16295 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16298 case SAMPLE_exit_open:
16299 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16302 case SAMPLE_exit_leave:
16303 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16306 case SAMPLE_dynamite:
16307 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16311 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16315 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16319 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16323 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16327 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16331 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16335 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16340 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
16342 int element = map_element_SP_to_RND(element_sp);
16343 int action = map_action_SP_to_RND(action_sp);
16344 int offset = (setup.sp_show_border_elements ? 0 : 1);
16345 int x = xx - offset;
16346 int y = yy - offset;
16349 printf("::: %d -> %d\n", element_sp, action_sp);
16352 PlayLevelSoundElementAction(x, y, element, action);
16356 void ChangeTime(int value)
16358 int *time = (game.no_time_limit ? &TimePlayed : &TimeLeft);
16362 /* EMC game engine uses value from time counter of RND game engine */
16363 level.native_em_level->lev->time = *time;
16365 DrawGameValue_Time(*time);
16368 void RaiseScore(int value)
16370 /* EMC game engine and RND game engine have separate score counters */
16371 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
16372 &level.native_em_level->lev->score : &local_player->score);
16376 DrawGameValue_Score(*score);
16380 void RaiseScore(int value)
16382 local_player->score += value;
16385 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16387 DisplayGameControlValues();
16389 DrawGameValue_Score(local_player->score);
16393 void RaiseScoreElement(int element)
16398 case EL_BD_DIAMOND:
16399 case EL_EMERALD_YELLOW:
16400 case EL_EMERALD_RED:
16401 case EL_EMERALD_PURPLE:
16402 case EL_SP_INFOTRON:
16403 RaiseScore(level.score[SC_EMERALD]);
16406 RaiseScore(level.score[SC_DIAMOND]);
16409 RaiseScore(level.score[SC_CRYSTAL]);
16412 RaiseScore(level.score[SC_PEARL]);
16415 case EL_BD_BUTTERFLY:
16416 case EL_SP_ELECTRON:
16417 RaiseScore(level.score[SC_BUG]);
16420 case EL_BD_FIREFLY:
16421 case EL_SP_SNIKSNAK:
16422 RaiseScore(level.score[SC_SPACESHIP]);
16425 case EL_DARK_YAMYAM:
16426 RaiseScore(level.score[SC_YAMYAM]);
16429 RaiseScore(level.score[SC_ROBOT]);
16432 RaiseScore(level.score[SC_PACMAN]);
16435 RaiseScore(level.score[SC_NUT]);
16438 case EL_EM_DYNAMITE:
16439 case EL_SP_DISK_RED:
16440 case EL_DYNABOMB_INCREASE_NUMBER:
16441 case EL_DYNABOMB_INCREASE_SIZE:
16442 case EL_DYNABOMB_INCREASE_POWER:
16443 RaiseScore(level.score[SC_DYNAMITE]);
16445 case EL_SHIELD_NORMAL:
16446 case EL_SHIELD_DEADLY:
16447 RaiseScore(level.score[SC_SHIELD]);
16449 case EL_EXTRA_TIME:
16450 RaiseScore(level.extra_time_score);
16464 case EL_DC_KEY_WHITE:
16465 RaiseScore(level.score[SC_KEY]);
16468 RaiseScore(element_info[element].collect_score);
16473 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16475 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16477 #if defined(NETWORK_AVALIABLE)
16478 if (options.network)
16479 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16488 FadeSkipNextFadeIn();
16490 fading = fading_none;
16494 OpenDoor(DOOR_CLOSE_1);
16497 game_status = GAME_MODE_MAIN;
16500 DrawAndFadeInMainMenu(REDRAW_FIELD);
16508 FadeOut(REDRAW_FIELD);
16511 game_status = GAME_MODE_MAIN;
16513 DrawAndFadeInMainMenu(REDRAW_FIELD);
16517 else /* continue playing the game */
16519 if (tape.playing && tape.deactivate_display)
16520 TapeDeactivateDisplayOff(TRUE);
16522 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16524 if (tape.playing && tape.deactivate_display)
16525 TapeDeactivateDisplayOn();
16529 void RequestQuitGame(boolean ask_if_really_quit)
16531 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16532 boolean skip_request = AllPlayersGone || quick_quit;
16534 RequestQuitGameExt(skip_request, quick_quit,
16535 "Do you really want to quit the game ?");
16539 /* ------------------------------------------------------------------------- */
16540 /* random generator functions */
16541 /* ------------------------------------------------------------------------- */
16543 unsigned int InitEngineRandom_RND(int seed)
16545 game.num_random_calls = 0;
16548 unsigned int rnd_seed = InitEngineRandom(seed);
16550 printf("::: START RND: %d\n", rnd_seed);
16555 return InitEngineRandom(seed);
16561 unsigned int RND(int max)
16565 game.num_random_calls++;
16567 return GetEngineRandom(max);
16574 /* ------------------------------------------------------------------------- */
16575 /* game engine snapshot handling functions */
16576 /* ------------------------------------------------------------------------- */
16578 struct EngineSnapshotInfo
16580 /* runtime values for custom element collect score */
16581 int collect_score[NUM_CUSTOM_ELEMENTS];
16583 /* runtime values for group element choice position */
16584 int choice_pos[NUM_GROUP_ELEMENTS];
16586 /* runtime values for belt position animations */
16587 int belt_graphic[4][NUM_BELT_PARTS];
16588 int belt_anim_mode[4][NUM_BELT_PARTS];
16591 static struct EngineSnapshotInfo engine_snapshot_rnd;
16592 static char *snapshot_level_identifier = NULL;
16593 static int snapshot_level_nr = -1;
16595 static void SaveEngineSnapshotValues_RND()
16597 static int belt_base_active_element[4] =
16599 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16600 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16601 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16602 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16606 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16608 int element = EL_CUSTOM_START + i;
16610 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16613 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16615 int element = EL_GROUP_START + i;
16617 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16620 for (i = 0; i < 4; i++)
16622 for (j = 0; j < NUM_BELT_PARTS; j++)
16624 int element = belt_base_active_element[i] + j;
16625 int graphic = el2img(element);
16626 int anim_mode = graphic_info[graphic].anim_mode;
16628 engine_snapshot_rnd.belt_graphic[i][j] = graphic;
16629 engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
16634 static void LoadEngineSnapshotValues_RND()
16636 unsigned int num_random_calls = game.num_random_calls;
16639 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16641 int element = EL_CUSTOM_START + i;
16643 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16646 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16648 int element = EL_GROUP_START + i;
16650 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16653 for (i = 0; i < 4; i++)
16655 for (j = 0; j < NUM_BELT_PARTS; j++)
16657 int graphic = engine_snapshot_rnd.belt_graphic[i][j];
16658 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
16660 graphic_info[graphic].anim_mode = anim_mode;
16664 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16666 InitRND(tape.random_seed);
16667 for (i = 0; i < num_random_calls; i++)
16671 if (game.num_random_calls != num_random_calls)
16673 Error(ERR_INFO, "number of random calls out of sync");
16674 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16675 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16676 Error(ERR_EXIT, "this should not happen -- please debug");
16680 void SaveEngineSnapshot()
16682 /* do not save snapshots from editor */
16683 if (level_editor_test_game)
16686 /* free previous snapshot buffers, if needed */
16687 FreeEngineSnapshotBuffers();
16689 /* copy some special values to a structure better suited for the snapshot */
16691 SaveEngineSnapshotValues_RND();
16692 SaveEngineSnapshotValues_EM();
16693 SaveEngineSnapshotValues_SP();
16695 /* save values stored in special snapshot structure */
16697 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16698 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16699 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16701 /* save further RND engine values */
16703 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16704 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16705 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16707 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16708 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16709 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16710 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16712 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16713 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16714 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16715 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16716 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16718 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16719 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16720 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16722 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16724 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16726 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16727 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16729 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16730 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16731 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16732 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16733 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16734 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16735 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16736 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16737 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16738 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16739 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16740 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16741 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16742 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16743 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16744 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16745 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16746 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16748 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16749 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16751 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16752 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16753 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16755 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16756 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16758 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16759 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16760 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16761 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16762 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16764 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16765 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16767 /* save level identification information */
16769 setString(&snapshot_level_identifier, leveldir_current->identifier);
16770 snapshot_level_nr = level_nr;
16773 ListNode *node = engine_snapshot_list_rnd;
16776 while (node != NULL)
16778 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16783 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16787 void LoadEngineSnapshot()
16789 /* restore generically stored snapshot buffers */
16791 LoadEngineSnapshotBuffers();
16793 /* restore special values from snapshot structure */
16795 LoadEngineSnapshotValues_RND();
16796 LoadEngineSnapshotValues_EM();
16797 LoadEngineSnapshotValues_SP();
16800 boolean CheckEngineSnapshot()
16802 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16803 snapshot_level_nr == level_nr);
16807 /* ---------- new game button stuff ---------------------------------------- */
16815 } gamebutton_info[NUM_GAME_BUTTONS] =
16818 IMG_GAME_BUTTON_GFX_STOP, &game.button.stop,
16819 GAME_CTRL_ID_STOP, "stop game"
16822 IMG_GAME_BUTTON_GFX_PAUSE, &game.button.pause,
16823 GAME_CTRL_ID_PAUSE, "pause game"
16826 IMG_GAME_BUTTON_GFX_PLAY, &game.button.play,
16827 GAME_CTRL_ID_PLAY, "play game"
16830 IMG_GAME_BUTTON_GFX_SOUND_MUSIC, &game.button.sound_music,
16831 SOUND_CTRL_ID_MUSIC, "background music on/off"
16834 IMG_GAME_BUTTON_GFX_SOUND_LOOPS, &game.button.sound_loops,
16835 SOUND_CTRL_ID_LOOPS, "sound loops on/off"
16838 IMG_GAME_BUTTON_GFX_SOUND_SIMPLE, &game.button.sound_simple,
16839 SOUND_CTRL_ID_SIMPLE, "normal sounds on/off"
16843 void CreateGameButtons()
16847 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16849 struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
16850 struct Rect *pos = gamebutton_info[i].pos;
16851 struct GadgetInfo *gi;
16854 unsigned int event_mask;
16855 int gd_x = gfx->src_x;
16856 int gd_y = gfx->src_y;
16857 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
16858 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
16859 int gd_xa = gfx->src_x + gfx->active_xoffset;
16860 int gd_ya = gfx->src_y + gfx->active_yoffset;
16861 int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16862 int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16865 if (id == GAME_CTRL_ID_STOP ||
16866 id == GAME_CTRL_ID_PAUSE ||
16867 id == GAME_CTRL_ID_PLAY)
16869 button_type = GD_TYPE_NORMAL_BUTTON;
16871 event_mask = GD_EVENT_RELEASED;
16875 button_type = GD_TYPE_CHECK_BUTTON;
16877 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16878 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16879 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16880 event_mask = GD_EVENT_PRESSED;
16883 gi = CreateGadget(GDI_CUSTOM_ID, id,
16884 GDI_INFO_TEXT, gamebutton_info[i].infotext,
16885 GDI_X, DX + pos->x,
16886 GDI_Y, DY + pos->y,
16887 GDI_WIDTH, gfx->width,
16888 GDI_HEIGHT, gfx->height,
16889 GDI_TYPE, button_type,
16890 GDI_STATE, GD_BUTTON_UNPRESSED,
16891 GDI_CHECKED, checked,
16892 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16893 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16894 GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16895 GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16896 GDI_DIRECT_DRAW, FALSE,
16897 GDI_EVENT_MASK, event_mask,
16898 GDI_CALLBACK_ACTION, HandleGameButtons,
16902 Error(ERR_EXIT, "cannot create gadget");
16904 game_gadget[id] = gi;
16908 void FreeGameButtons()
16912 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16913 FreeGadget(game_gadget[i]);
16916 static void MapGameButtons()
16920 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16921 MapGadget(game_gadget[i]);
16924 void UnmapGameButtons()
16928 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16929 UnmapGadget(game_gadget[i]);
16932 void RedrawGameButtons()
16936 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16937 RedrawGadget(game_gadget[i]);
16940 static void HandleGameButtonsExt(int id)
16942 if (game_status != GAME_MODE_PLAYING)
16947 case GAME_CTRL_ID_STOP:
16951 RequestQuitGame(TRUE);
16954 case GAME_CTRL_ID_PAUSE:
16955 if (options.network)
16957 #if defined(NETWORK_AVALIABLE)
16959 SendToServer_ContinuePlaying();
16961 SendToServer_PausePlaying();
16965 TapeTogglePause(TAPE_TOGGLE_MANUAL);
16968 case GAME_CTRL_ID_PLAY:
16971 #if defined(NETWORK_AVALIABLE)
16972 if (options.network)
16973 SendToServer_ContinuePlaying();
16977 tape.pausing = FALSE;
16978 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16983 case SOUND_CTRL_ID_MUSIC:
16984 if (setup.sound_music)
16986 setup.sound_music = FALSE;
16990 else if (audio.music_available)
16992 setup.sound = setup.sound_music = TRUE;
16994 SetAudioMode(setup.sound);
17000 case SOUND_CTRL_ID_LOOPS:
17001 if (setup.sound_loops)
17002 setup.sound_loops = FALSE;
17003 else if (audio.loops_available)
17005 setup.sound = setup.sound_loops = TRUE;
17007 SetAudioMode(setup.sound);
17011 case SOUND_CTRL_ID_SIMPLE:
17012 if (setup.sound_simple)
17013 setup.sound_simple = FALSE;
17014 else if (audio.sound_available)
17016 setup.sound = setup.sound_simple = TRUE;
17018 SetAudioMode(setup.sound);
17027 static void HandleGameButtons(struct GadgetInfo *gi)
17029 HandleGameButtonsExt(gi->custom_id);
17032 void HandleSoundButtonKeys(Key key)
17035 if (key == setup.shortcut.sound_simple)
17036 ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
17037 else if (key == setup.shortcut.sound_loops)
17038 ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
17039 else if (key == setup.shortcut.sound_music)
17040 ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
17042 if (key == setup.shortcut.sound_simple)
17043 HandleGameButtonsExt(SOUND_CTRL_ID_SIMPLE);
17044 else if (key == setup.shortcut.sound_loops)
17045 HandleGameButtonsExt(SOUND_CTRL_ID_LOOPS);
17046 else if (key == setup.shortcut.sound_music)
17047 HandleGameButtonsExt(SOUND_CTRL_ID_MUSIC);