1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include "libgame/libgame.h"
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF ( 1)
30 #define USE_NEW_SP_SLIPPERY (USE_NEW_STUFF * 1)
31 #define USE_NEW_CUSTOM_VALUE (USE_NEW_STUFF * 1)
32 #define USE_NEW_PLAYER_ANIM (USE_NEW_STUFF * 1)
33 #define USE_NEW_ALL_SLIPPERY (USE_NEW_STUFF * 1)
34 #define USE_NEW_PLAYER_SPEED (USE_NEW_STUFF * 1)
35 #define USE_NEW_DELAYED_ACTION (USE_NEW_STUFF * 1)
36 #define USE_NEW_SNAP_DELAY (USE_NEW_STUFF * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
39 #define USE_FIXED_DONT_RUN_INTO (USE_NEW_STUFF * 1)
40 #define USE_NEW_SPRING_BUMPER (USE_NEW_STUFF * 1)
41 #define USE_STOP_CHANGED_ELEMENTS (USE_NEW_STUFF * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX (USE_NEW_STUFF * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING (USE_NEW_STUFF * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION (USE_NEW_STUFF * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES (USE_NEW_STUFF * 1)
46 #define USE_PLAYER_GRAVITY (USE_NEW_STUFF * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX (USE_NEW_STUFF * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX (USE_NEW_STUFF * 0)
50 #define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0)
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF * 1)
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX (USE_NEW_STUFF * 1)
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING (USE_NEW_STUFF * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK (USE_NEW_STUFF * 1)
59 #define USE_FIX_KILLED_BY_NON_WALKABLE (USE_NEW_STUFF * 1)
60 #define USE_FIX_IMPACT_COLLISION (USE_NEW_STUFF * 1)
61 #define USE_FIX_CE_ACTION_WITH_PLAYER (USE_NEW_STUFF * 1)
62 #define USE_FIX_NO_ACTION_AFTER_CHANGE (USE_NEW_STUFF * 1)
64 #define USE_PLAYER_REANIMATION (USE_NEW_STUFF * 1)
66 #define USE_GFX_RESET_WHEN_NOT_MOVING (USE_NEW_STUFF * 1)
68 #define USE_NEW_PLAYER_ASSIGNMENTS (USE_NEW_STUFF * 1)
70 #define USE_DELAYED_GFX_REDRAW (USE_NEW_STUFF * 0)
72 #if USE_DELAYED_GFX_REDRAW
73 #define TEST_DrawLevelField(x, y) \
74 GfxRedraw[x][y] |= GFX_REDRAW_TILE
75 #define TEST_DrawLevelFieldCrumbled(x, y) \
76 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
77 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y) \
78 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
79 #define TEST_DrawTwinkleOnField(x, y) \
80 GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
82 #define TEST_DrawLevelField(x, y) \
84 #define TEST_DrawLevelFieldCrumbled(x, y) \
85 DrawLevelFieldCrumbled(x, y)
86 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y) \
87 DrawLevelFieldCrumbledNeighbours(x, y)
88 #define TEST_DrawTwinkleOnField(x, y) \
89 DrawTwinkleOnField(x, y)
98 /* for MovePlayer() */
99 #define MP_NO_ACTION 0
102 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
104 /* for ScrollPlayer() */
105 #define SCROLL_INIT 0
106 #define SCROLL_GO_ON 1
108 /* for Bang()/Explode() */
109 #define EX_PHASE_START 0
110 #define EX_TYPE_NONE 0
111 #define EX_TYPE_NORMAL (1 << 0)
112 #define EX_TYPE_CENTER (1 << 1)
113 #define EX_TYPE_BORDER (1 << 2)
114 #define EX_TYPE_CROSS (1 << 3)
115 #define EX_TYPE_DYNA (1 << 4)
116 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
118 #define PANEL_OFF() (local_player->LevelSolved_PanelOff)
119 #define PANEL_DEACTIVATED(p) ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
120 #define PANEL_XPOS(p) (DX + ALIGNED_TEXT_XPOS(p))
121 #define PANEL_YPOS(p) (DY + ALIGNED_TEXT_YPOS(p))
123 /* special positions in the game control window (relative to control window) */
124 #define XX_LEVEL1 (PANEL_XPOS(game.panel.level))
125 #define XX_LEVEL2 (PANEL_XPOS(game.panel.level) - 1)
126 #define XX_LEVEL (PANEL_XPOS(game.panel.level))
127 #define YY_LEVEL (PANEL_YPOS(game.panel.level))
128 #define XX_EMERALDS (PANEL_XPOS(game.panel.gems))
129 #define YY_EMERALDS (PANEL_YPOS(game.panel.gems))
130 #define XX_DYNAMITE (PANEL_XPOS(game.panel.inventory))
131 #define YY_DYNAMITE (PANEL_YPOS(game.panel.inventory))
132 #define XX_KEYS (PANEL_XPOS(game.panel.keys))
133 #define YY_KEYS (PANEL_YPOS(game.panel.keys))
134 #define XX_SCORE (PANEL_XPOS(game.panel.score))
135 #define YY_SCORE (PANEL_YPOS(game.panel.score))
136 #define XX_TIME1 (PANEL_XPOS(game.panel.time))
137 #define XX_TIME2 (PANEL_XPOS(game.panel.time) + 1)
138 #define XX_TIME (PANEL_XPOS(game.panel.time))
139 #define YY_TIME (PANEL_YPOS(game.panel.time))
141 /* special positions in the game control window (relative to main window) */
142 #define DX_LEVEL1 (DX + XX_LEVEL1)
143 #define DX_LEVEL2 (DX + XX_LEVEL2)
144 #define DX_LEVEL (DX + XX_LEVEL)
145 #define DY_LEVEL (DY + YY_LEVEL)
146 #define DX_EMERALDS (DX + XX_EMERALDS)
147 #define DY_EMERALDS (DY + YY_EMERALDS)
148 #define DX_DYNAMITE (DX + XX_DYNAMITE)
149 #define DY_DYNAMITE (DY + YY_DYNAMITE)
150 #define DX_KEYS (DX + XX_KEYS)
151 #define DY_KEYS (DY + YY_KEYS)
152 #define DX_SCORE (DX + XX_SCORE)
153 #define DY_SCORE (DY + YY_SCORE)
154 #define DX_TIME1 (DX + XX_TIME1)
155 #define DX_TIME2 (DX + XX_TIME2)
156 #define DX_TIME (DX + XX_TIME)
157 #define DY_TIME (DY + YY_TIME)
160 /* game panel display and control definitions */
162 #define GAME_PANEL_LEVEL_NUMBER 0
163 #define GAME_PANEL_GEMS 1
164 #define GAME_PANEL_INVENTORY_COUNT 2
165 #define GAME_PANEL_INVENTORY_FIRST_1 3
166 #define GAME_PANEL_INVENTORY_FIRST_2 4
167 #define GAME_PANEL_INVENTORY_FIRST_3 5
168 #define GAME_PANEL_INVENTORY_FIRST_4 6
169 #define GAME_PANEL_INVENTORY_FIRST_5 7
170 #define GAME_PANEL_INVENTORY_FIRST_6 8
171 #define GAME_PANEL_INVENTORY_FIRST_7 9
172 #define GAME_PANEL_INVENTORY_FIRST_8 10
173 #define GAME_PANEL_INVENTORY_LAST_1 11
174 #define GAME_PANEL_INVENTORY_LAST_2 12
175 #define GAME_PANEL_INVENTORY_LAST_3 13
176 #define GAME_PANEL_INVENTORY_LAST_4 14
177 #define GAME_PANEL_INVENTORY_LAST_5 15
178 #define GAME_PANEL_INVENTORY_LAST_6 16
179 #define GAME_PANEL_INVENTORY_LAST_7 17
180 #define GAME_PANEL_INVENTORY_LAST_8 18
181 #define GAME_PANEL_KEY_1 19
182 #define GAME_PANEL_KEY_2 20
183 #define GAME_PANEL_KEY_3 21
184 #define GAME_PANEL_KEY_4 22
185 #define GAME_PANEL_KEY_5 23
186 #define GAME_PANEL_KEY_6 24
187 #define GAME_PANEL_KEY_7 25
188 #define GAME_PANEL_KEY_8 26
189 #define GAME_PANEL_KEY_WHITE 27
190 #define GAME_PANEL_KEY_WHITE_COUNT 28
191 #define GAME_PANEL_SCORE 29
192 #define GAME_PANEL_HIGHSCORE 30
193 #define GAME_PANEL_TIME 31
194 #define GAME_PANEL_TIME_HH 32
195 #define GAME_PANEL_TIME_MM 33
196 #define GAME_PANEL_TIME_SS 34
197 #define GAME_PANEL_FRAME 35
198 #define GAME_PANEL_SHIELD_NORMAL 36
199 #define GAME_PANEL_SHIELD_NORMAL_TIME 37
200 #define GAME_PANEL_SHIELD_DEADLY 38
201 #define GAME_PANEL_SHIELD_DEADLY_TIME 39
202 #define GAME_PANEL_EXIT 40
203 #define GAME_PANEL_EMC_MAGIC_BALL 41
204 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH 42
205 #define GAME_PANEL_LIGHT_SWITCH 43
206 #define GAME_PANEL_LIGHT_SWITCH_TIME 44
207 #define GAME_PANEL_TIMEGATE_SWITCH 45
208 #define GAME_PANEL_TIMEGATE_SWITCH_TIME 46
209 #define GAME_PANEL_SWITCHGATE_SWITCH 47
210 #define GAME_PANEL_EMC_LENSES 48
211 #define GAME_PANEL_EMC_LENSES_TIME 49
212 #define GAME_PANEL_EMC_MAGNIFIER 50
213 #define GAME_PANEL_EMC_MAGNIFIER_TIME 51
214 #define GAME_PANEL_BALLOON_SWITCH 52
215 #define GAME_PANEL_DYNABOMB_NUMBER 53
216 #define GAME_PANEL_DYNABOMB_SIZE 54
217 #define GAME_PANEL_DYNABOMB_POWER 55
218 #define GAME_PANEL_PENGUINS 56
219 #define GAME_PANEL_SOKOBAN_OBJECTS 57
220 #define GAME_PANEL_SOKOBAN_FIELDS 58
221 #define GAME_PANEL_ROBOT_WHEEL 59
222 #define GAME_PANEL_CONVEYOR_BELT_1 60
223 #define GAME_PANEL_CONVEYOR_BELT_2 61
224 #define GAME_PANEL_CONVEYOR_BELT_3 62
225 #define GAME_PANEL_CONVEYOR_BELT_4 63
226 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH 64
227 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH 65
228 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH 66
229 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH 67
230 #define GAME_PANEL_MAGIC_WALL 68
231 #define GAME_PANEL_MAGIC_WALL_TIME 69
232 #define GAME_PANEL_GRAVITY_STATE 70
233 #define GAME_PANEL_GRAPHIC_1 71
234 #define GAME_PANEL_GRAPHIC_2 72
235 #define GAME_PANEL_GRAPHIC_3 73
236 #define GAME_PANEL_GRAPHIC_4 74
237 #define GAME_PANEL_GRAPHIC_5 75
238 #define GAME_PANEL_GRAPHIC_6 76
239 #define GAME_PANEL_GRAPHIC_7 77
240 #define GAME_PANEL_GRAPHIC_8 78
241 #define GAME_PANEL_ELEMENT_1 79
242 #define GAME_PANEL_ELEMENT_2 80
243 #define GAME_PANEL_ELEMENT_3 81
244 #define GAME_PANEL_ELEMENT_4 82
245 #define GAME_PANEL_ELEMENT_5 83
246 #define GAME_PANEL_ELEMENT_6 84
247 #define GAME_PANEL_ELEMENT_7 85
248 #define GAME_PANEL_ELEMENT_8 86
249 #define GAME_PANEL_ELEMENT_COUNT_1 87
250 #define GAME_PANEL_ELEMENT_COUNT_2 88
251 #define GAME_PANEL_ELEMENT_COUNT_3 89
252 #define GAME_PANEL_ELEMENT_COUNT_4 90
253 #define GAME_PANEL_ELEMENT_COUNT_5 91
254 #define GAME_PANEL_ELEMENT_COUNT_6 92
255 #define GAME_PANEL_ELEMENT_COUNT_7 93
256 #define GAME_PANEL_ELEMENT_COUNT_8 94
257 #define GAME_PANEL_CE_SCORE_1 95
258 #define GAME_PANEL_CE_SCORE_2 96
259 #define GAME_PANEL_CE_SCORE_3 97
260 #define GAME_PANEL_CE_SCORE_4 98
261 #define GAME_PANEL_CE_SCORE_5 99
262 #define GAME_PANEL_CE_SCORE_6 100
263 #define GAME_PANEL_CE_SCORE_7 101
264 #define GAME_PANEL_CE_SCORE_8 102
265 #define GAME_PANEL_CE_SCORE_1_ELEMENT 103
266 #define GAME_PANEL_CE_SCORE_2_ELEMENT 104
267 #define GAME_PANEL_CE_SCORE_3_ELEMENT 105
268 #define GAME_PANEL_CE_SCORE_4_ELEMENT 106
269 #define GAME_PANEL_CE_SCORE_5_ELEMENT 107
270 #define GAME_PANEL_CE_SCORE_6_ELEMENT 108
271 #define GAME_PANEL_CE_SCORE_7_ELEMENT 109
272 #define GAME_PANEL_CE_SCORE_8_ELEMENT 110
273 #define GAME_PANEL_PLAYER_NAME 111
274 #define GAME_PANEL_LEVEL_NAME 112
275 #define GAME_PANEL_LEVEL_AUTHOR 113
277 #define NUM_GAME_PANEL_CONTROLS 114
279 struct GamePanelOrderInfo
285 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
287 struct GamePanelControlInfo
291 struct TextPosInfo *pos;
294 int value, last_value;
295 int frame, last_frame;
300 static struct GamePanelControlInfo game_panel_controls[] =
303 GAME_PANEL_LEVEL_NUMBER,
304 &game.panel.level_number,
313 GAME_PANEL_INVENTORY_COUNT,
314 &game.panel.inventory_count,
318 GAME_PANEL_INVENTORY_FIRST_1,
319 &game.panel.inventory_first[0],
323 GAME_PANEL_INVENTORY_FIRST_2,
324 &game.panel.inventory_first[1],
328 GAME_PANEL_INVENTORY_FIRST_3,
329 &game.panel.inventory_first[2],
333 GAME_PANEL_INVENTORY_FIRST_4,
334 &game.panel.inventory_first[3],
338 GAME_PANEL_INVENTORY_FIRST_5,
339 &game.panel.inventory_first[4],
343 GAME_PANEL_INVENTORY_FIRST_6,
344 &game.panel.inventory_first[5],
348 GAME_PANEL_INVENTORY_FIRST_7,
349 &game.panel.inventory_first[6],
353 GAME_PANEL_INVENTORY_FIRST_8,
354 &game.panel.inventory_first[7],
358 GAME_PANEL_INVENTORY_LAST_1,
359 &game.panel.inventory_last[0],
363 GAME_PANEL_INVENTORY_LAST_2,
364 &game.panel.inventory_last[1],
368 GAME_PANEL_INVENTORY_LAST_3,
369 &game.panel.inventory_last[2],
373 GAME_PANEL_INVENTORY_LAST_4,
374 &game.panel.inventory_last[3],
378 GAME_PANEL_INVENTORY_LAST_5,
379 &game.panel.inventory_last[4],
383 GAME_PANEL_INVENTORY_LAST_6,
384 &game.panel.inventory_last[5],
388 GAME_PANEL_INVENTORY_LAST_7,
389 &game.panel.inventory_last[6],
393 GAME_PANEL_INVENTORY_LAST_8,
394 &game.panel.inventory_last[7],
438 GAME_PANEL_KEY_WHITE,
439 &game.panel.key_white,
443 GAME_PANEL_KEY_WHITE_COUNT,
444 &game.panel.key_white_count,
453 GAME_PANEL_HIGHSCORE,
454 &game.panel.highscore,
483 GAME_PANEL_SHIELD_NORMAL,
484 &game.panel.shield_normal,
488 GAME_PANEL_SHIELD_NORMAL_TIME,
489 &game.panel.shield_normal_time,
493 GAME_PANEL_SHIELD_DEADLY,
494 &game.panel.shield_deadly,
498 GAME_PANEL_SHIELD_DEADLY_TIME,
499 &game.panel.shield_deadly_time,
508 GAME_PANEL_EMC_MAGIC_BALL,
509 &game.panel.emc_magic_ball,
513 GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
514 &game.panel.emc_magic_ball_switch,
518 GAME_PANEL_LIGHT_SWITCH,
519 &game.panel.light_switch,
523 GAME_PANEL_LIGHT_SWITCH_TIME,
524 &game.panel.light_switch_time,
528 GAME_PANEL_TIMEGATE_SWITCH,
529 &game.panel.timegate_switch,
533 GAME_PANEL_TIMEGATE_SWITCH_TIME,
534 &game.panel.timegate_switch_time,
538 GAME_PANEL_SWITCHGATE_SWITCH,
539 &game.panel.switchgate_switch,
543 GAME_PANEL_EMC_LENSES,
544 &game.panel.emc_lenses,
548 GAME_PANEL_EMC_LENSES_TIME,
549 &game.panel.emc_lenses_time,
553 GAME_PANEL_EMC_MAGNIFIER,
554 &game.panel.emc_magnifier,
558 GAME_PANEL_EMC_MAGNIFIER_TIME,
559 &game.panel.emc_magnifier_time,
563 GAME_PANEL_BALLOON_SWITCH,
564 &game.panel.balloon_switch,
568 GAME_PANEL_DYNABOMB_NUMBER,
569 &game.panel.dynabomb_number,
573 GAME_PANEL_DYNABOMB_SIZE,
574 &game.panel.dynabomb_size,
578 GAME_PANEL_DYNABOMB_POWER,
579 &game.panel.dynabomb_power,
584 &game.panel.penguins,
588 GAME_PANEL_SOKOBAN_OBJECTS,
589 &game.panel.sokoban_objects,
593 GAME_PANEL_SOKOBAN_FIELDS,
594 &game.panel.sokoban_fields,
598 GAME_PANEL_ROBOT_WHEEL,
599 &game.panel.robot_wheel,
603 GAME_PANEL_CONVEYOR_BELT_1,
604 &game.panel.conveyor_belt[0],
608 GAME_PANEL_CONVEYOR_BELT_2,
609 &game.panel.conveyor_belt[1],
613 GAME_PANEL_CONVEYOR_BELT_3,
614 &game.panel.conveyor_belt[2],
618 GAME_PANEL_CONVEYOR_BELT_4,
619 &game.panel.conveyor_belt[3],
623 GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
624 &game.panel.conveyor_belt_switch[0],
628 GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
629 &game.panel.conveyor_belt_switch[1],
633 GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
634 &game.panel.conveyor_belt_switch[2],
638 GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
639 &game.panel.conveyor_belt_switch[3],
643 GAME_PANEL_MAGIC_WALL,
644 &game.panel.magic_wall,
648 GAME_PANEL_MAGIC_WALL_TIME,
649 &game.panel.magic_wall_time,
653 GAME_PANEL_GRAVITY_STATE,
654 &game.panel.gravity_state,
658 GAME_PANEL_GRAPHIC_1,
659 &game.panel.graphic[0],
663 GAME_PANEL_GRAPHIC_2,
664 &game.panel.graphic[1],
668 GAME_PANEL_GRAPHIC_3,
669 &game.panel.graphic[2],
673 GAME_PANEL_GRAPHIC_4,
674 &game.panel.graphic[3],
678 GAME_PANEL_GRAPHIC_5,
679 &game.panel.graphic[4],
683 GAME_PANEL_GRAPHIC_6,
684 &game.panel.graphic[5],
688 GAME_PANEL_GRAPHIC_7,
689 &game.panel.graphic[6],
693 GAME_PANEL_GRAPHIC_8,
694 &game.panel.graphic[7],
698 GAME_PANEL_ELEMENT_1,
699 &game.panel.element[0],
703 GAME_PANEL_ELEMENT_2,
704 &game.panel.element[1],
708 GAME_PANEL_ELEMENT_3,
709 &game.panel.element[2],
713 GAME_PANEL_ELEMENT_4,
714 &game.panel.element[3],
718 GAME_PANEL_ELEMENT_5,
719 &game.panel.element[4],
723 GAME_PANEL_ELEMENT_6,
724 &game.panel.element[5],
728 GAME_PANEL_ELEMENT_7,
729 &game.panel.element[6],
733 GAME_PANEL_ELEMENT_8,
734 &game.panel.element[7],
738 GAME_PANEL_ELEMENT_COUNT_1,
739 &game.panel.element_count[0],
743 GAME_PANEL_ELEMENT_COUNT_2,
744 &game.panel.element_count[1],
748 GAME_PANEL_ELEMENT_COUNT_3,
749 &game.panel.element_count[2],
753 GAME_PANEL_ELEMENT_COUNT_4,
754 &game.panel.element_count[3],
758 GAME_PANEL_ELEMENT_COUNT_5,
759 &game.panel.element_count[4],
763 GAME_PANEL_ELEMENT_COUNT_6,
764 &game.panel.element_count[5],
768 GAME_PANEL_ELEMENT_COUNT_7,
769 &game.panel.element_count[6],
773 GAME_PANEL_ELEMENT_COUNT_8,
774 &game.panel.element_count[7],
778 GAME_PANEL_CE_SCORE_1,
779 &game.panel.ce_score[0],
783 GAME_PANEL_CE_SCORE_2,
784 &game.panel.ce_score[1],
788 GAME_PANEL_CE_SCORE_3,
789 &game.panel.ce_score[2],
793 GAME_PANEL_CE_SCORE_4,
794 &game.panel.ce_score[3],
798 GAME_PANEL_CE_SCORE_5,
799 &game.panel.ce_score[4],
803 GAME_PANEL_CE_SCORE_6,
804 &game.panel.ce_score[5],
808 GAME_PANEL_CE_SCORE_7,
809 &game.panel.ce_score[6],
813 GAME_PANEL_CE_SCORE_8,
814 &game.panel.ce_score[7],
818 GAME_PANEL_CE_SCORE_1_ELEMENT,
819 &game.panel.ce_score_element[0],
823 GAME_PANEL_CE_SCORE_2_ELEMENT,
824 &game.panel.ce_score_element[1],
828 GAME_PANEL_CE_SCORE_3_ELEMENT,
829 &game.panel.ce_score_element[2],
833 GAME_PANEL_CE_SCORE_4_ELEMENT,
834 &game.panel.ce_score_element[3],
838 GAME_PANEL_CE_SCORE_5_ELEMENT,
839 &game.panel.ce_score_element[4],
843 GAME_PANEL_CE_SCORE_6_ELEMENT,
844 &game.panel.ce_score_element[5],
848 GAME_PANEL_CE_SCORE_7_ELEMENT,
849 &game.panel.ce_score_element[6],
853 GAME_PANEL_CE_SCORE_8_ELEMENT,
854 &game.panel.ce_score_element[7],
858 GAME_PANEL_PLAYER_NAME,
859 &game.panel.player_name,
863 GAME_PANEL_LEVEL_NAME,
864 &game.panel.level_name,
868 GAME_PANEL_LEVEL_AUTHOR,
869 &game.panel.level_author,
882 /* values for delayed check of falling and moving elements and for collision */
883 #define CHECK_DELAY_MOVING 3
884 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
885 #define CHECK_DELAY_COLLISION 2
886 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
888 /* values for initial player move delay (initial delay counter value) */
889 #define INITIAL_MOVE_DELAY_OFF -1
890 #define INITIAL_MOVE_DELAY_ON 0
892 /* values for player movement speed (which is in fact a delay value) */
893 #define MOVE_DELAY_MIN_SPEED 32
894 #define MOVE_DELAY_NORMAL_SPEED 8
895 #define MOVE_DELAY_HIGH_SPEED 4
896 #define MOVE_DELAY_MAX_SPEED 1
898 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
899 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
901 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
902 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
904 /* values for other actions */
905 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
906 #define MOVE_STEPSIZE_MIN (1)
907 #define MOVE_STEPSIZE_MAX (TILEX)
909 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
910 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
912 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
914 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
915 RND(element_info[e].push_delay_random))
916 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
917 RND(element_info[e].drop_delay_random))
918 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
919 RND(element_info[e].move_delay_random))
920 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
921 (element_info[e].move_delay_random))
922 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
923 RND(element_info[e].ce_value_random_initial))
924 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
925 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
926 RND((c)->delay_random * (c)->delay_frames))
927 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
928 RND((c)->delay_random))
931 #define GET_VALID_RUNTIME_ELEMENT(e) \
932 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
934 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
935 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
936 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
937 (be) + (e) - EL_SELF)
939 #define GET_PLAYER_FROM_BITS(p) \
940 (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
942 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
943 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
944 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
945 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
946 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
947 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
948 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
949 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
950 RESOLVED_REFERENCE_ELEMENT(be, e) : \
953 #define CAN_GROW_INTO(e) \
954 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
956 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
957 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
960 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
961 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
962 (CAN_MOVE_INTO_ACID(e) && \
963 Feld[x][y] == EL_ACID) || \
966 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
967 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
968 (CAN_MOVE_INTO_ACID(e) && \
969 Feld[x][y] == EL_ACID) || \
972 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
973 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
975 (CAN_MOVE_INTO_ACID(e) && \
976 Feld[x][y] == EL_ACID) || \
977 (DONT_COLLIDE_WITH(e) && \
979 !PLAYER_ENEMY_PROTECTED(x, y))))
981 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
982 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
984 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
985 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
987 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
988 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
990 #define ANDROID_CAN_CLONE_FIELD(x, y) \
991 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
992 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
994 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
995 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
997 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
998 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
1000 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
1001 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
1003 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
1004 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
1006 #define PIG_CAN_ENTER_FIELD(e, x, y) \
1007 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
1009 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
1010 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
1011 Feld[x][y] == EL_EM_EXIT_OPEN || \
1012 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
1013 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
1014 IS_FOOD_PENGUIN(Feld[x][y])))
1015 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
1016 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1018 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
1019 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
1021 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
1022 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1024 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
1025 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
1026 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
1028 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
1030 #define CE_ENTER_FIELD_COND(e, x, y) \
1031 (!IS_PLAYER(x, y) && \
1032 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
1034 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
1035 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1037 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
1038 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1040 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
1041 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
1042 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
1043 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1045 /* game button identifiers */
1046 #define GAME_CTRL_ID_STOP 0
1047 #define GAME_CTRL_ID_PAUSE 1
1048 #define GAME_CTRL_ID_PLAY 2
1049 #define SOUND_CTRL_ID_MUSIC 3
1050 #define SOUND_CTRL_ID_LOOPS 4
1051 #define SOUND_CTRL_ID_SIMPLE 5
1053 #define NUM_GAME_BUTTONS 6
1056 /* forward declaration for internal use */
1058 static void CreateField(int, int, int);
1060 static void ResetGfxAnimation(int, int);
1062 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1063 static void AdvanceFrameAndPlayerCounters(int);
1065 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1066 static boolean MovePlayer(struct PlayerInfo *, int, int);
1067 static void ScrollPlayer(struct PlayerInfo *, int);
1068 static void ScrollScreen(struct PlayerInfo *, int);
1070 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1071 static boolean DigFieldByCE(int, int, int);
1072 static boolean SnapField(struct PlayerInfo *, int, int);
1073 static boolean DropElement(struct PlayerInfo *);
1075 static void InitBeltMovement(void);
1076 static void CloseAllOpenTimegates(void);
1077 static void CheckGravityMovement(struct PlayerInfo *);
1078 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1079 static void KillPlayerUnlessEnemyProtected(int, int);
1080 static void KillPlayerUnlessExplosionProtected(int, int);
1082 static void TestIfPlayerTouchesCustomElement(int, int);
1083 static void TestIfElementTouchesCustomElement(int, int);
1084 static void TestIfElementHitsCustomElement(int, int, int);
1086 static void TestIfElementSmashesCustomElement(int, int, int);
1089 static void HandleElementChange(int, int, int);
1090 static void ExecuteCustomElementAction(int, int, int, int);
1091 static boolean ChangeElement(int, int, int, int);
1093 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1094 #define CheckTriggeredElementChange(x, y, e, ev) \
1095 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1096 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
1097 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1098 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
1099 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1100 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
1101 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1103 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1104 #define CheckElementChange(x, y, e, te, ev) \
1105 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1106 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
1107 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1108 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
1109 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1111 static void PlayLevelSound(int, int, int);
1112 static void PlayLevelSoundNearest(int, int, int);
1113 static void PlayLevelSoundAction(int, int, int);
1114 static void PlayLevelSoundElementAction(int, int, int, int);
1115 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1116 static void PlayLevelSoundActionIfLoop(int, int, int);
1117 static void StopLevelSoundActionIfLoop(int, int, int);
1118 static void PlayLevelMusic();
1120 static void MapGameButtons();
1121 static void HandleGameButtons(struct GadgetInfo *);
1123 int AmoebeNachbarNr(int, int);
1124 void AmoebeUmwandeln(int, int);
1125 void ContinueMoving(int, int);
1126 void Bang(int, int);
1127 void InitMovDir(int, int);
1128 void InitAmoebaNr(int, int);
1129 int NewHiScore(void);
1131 void TestIfGoodThingHitsBadThing(int, int, int);
1132 void TestIfBadThingHitsGoodThing(int, int, int);
1133 void TestIfPlayerTouchesBadThing(int, int);
1134 void TestIfPlayerRunsIntoBadThing(int, int, int);
1135 void TestIfBadThingTouchesPlayer(int, int);
1136 void TestIfBadThingRunsIntoPlayer(int, int, int);
1137 void TestIfFriendTouchesBadThing(int, int);
1138 void TestIfBadThingTouchesFriend(int, int);
1139 void TestIfBadThingTouchesOtherBadThing(int, int);
1140 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1142 void KillPlayer(struct PlayerInfo *);
1143 void BuryPlayer(struct PlayerInfo *);
1144 void RemovePlayer(struct PlayerInfo *);
1146 static int getInvisibleActiveFromInvisibleElement(int);
1147 static int getInvisibleFromInvisibleActiveElement(int);
1149 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1151 /* for detection of endless loops, caused by custom element programming */
1152 /* (using maximal playfield width x 10 is just a rough approximation) */
1153 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
1155 #define RECURSION_LOOP_DETECTION_START(e, rc) \
1157 if (recursion_loop_detected) \
1160 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
1162 recursion_loop_detected = TRUE; \
1163 recursion_loop_element = (e); \
1166 recursion_loop_depth++; \
1169 #define RECURSION_LOOP_DETECTION_END() \
1171 recursion_loop_depth--; \
1174 static int recursion_loop_depth;
1175 static boolean recursion_loop_detected;
1176 static boolean recursion_loop_element;
1178 static int map_player_action[MAX_PLAYERS];
1181 /* ------------------------------------------------------------------------- */
1182 /* definition of elements that automatically change to other elements after */
1183 /* a specified time, eventually calling a function when changing */
1184 /* ------------------------------------------------------------------------- */
1186 /* forward declaration for changer functions */
1187 static void InitBuggyBase(int, int);
1188 static void WarnBuggyBase(int, int);
1190 static void InitTrap(int, int);
1191 static void ActivateTrap(int, int);
1192 static void ChangeActiveTrap(int, int);
1194 static void InitRobotWheel(int, int);
1195 static void RunRobotWheel(int, int);
1196 static void StopRobotWheel(int, int);
1198 static void InitTimegateWheel(int, int);
1199 static void RunTimegateWheel(int, int);
1201 static void InitMagicBallDelay(int, int);
1202 static void ActivateMagicBall(int, int);
1204 struct ChangingElementInfo
1209 void (*pre_change_function)(int x, int y);
1210 void (*change_function)(int x, int y);
1211 void (*post_change_function)(int x, int y);
1214 static struct ChangingElementInfo change_delay_list[] =
1249 EL_STEEL_EXIT_OPENING,
1257 EL_STEEL_EXIT_CLOSING,
1258 EL_STEEL_EXIT_CLOSED,
1285 EL_EM_STEEL_EXIT_OPENING,
1286 EL_EM_STEEL_EXIT_OPEN,
1293 EL_EM_STEEL_EXIT_CLOSING,
1297 EL_EM_STEEL_EXIT_CLOSED,
1321 EL_SWITCHGATE_OPENING,
1329 EL_SWITCHGATE_CLOSING,
1330 EL_SWITCHGATE_CLOSED,
1337 EL_TIMEGATE_OPENING,
1345 EL_TIMEGATE_CLOSING,
1354 EL_ACID_SPLASH_LEFT,
1362 EL_ACID_SPLASH_RIGHT,
1371 EL_SP_BUGGY_BASE_ACTIVATING,
1378 EL_SP_BUGGY_BASE_ACTIVATING,
1379 EL_SP_BUGGY_BASE_ACTIVE,
1386 EL_SP_BUGGY_BASE_ACTIVE,
1410 EL_ROBOT_WHEEL_ACTIVE,
1418 EL_TIMEGATE_SWITCH_ACTIVE,
1426 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1427 EL_DC_TIMEGATE_SWITCH,
1434 EL_EMC_MAGIC_BALL_ACTIVE,
1435 EL_EMC_MAGIC_BALL_ACTIVE,
1442 EL_EMC_SPRING_BUMPER_ACTIVE,
1443 EL_EMC_SPRING_BUMPER,
1450 EL_DIAGONAL_SHRINKING,
1458 EL_DIAGONAL_GROWING,
1479 int push_delay_fixed, push_delay_random;
1483 { EL_SPRING, 0, 0 },
1484 { EL_BALLOON, 0, 0 },
1486 { EL_SOKOBAN_OBJECT, 2, 0 },
1487 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1488 { EL_SATELLITE, 2, 0 },
1489 { EL_SP_DISK_YELLOW, 2, 0 },
1491 { EL_UNDEFINED, 0, 0 },
1499 move_stepsize_list[] =
1501 { EL_AMOEBA_DROP, 2 },
1502 { EL_AMOEBA_DROPPING, 2 },
1503 { EL_QUICKSAND_FILLING, 1 },
1504 { EL_QUICKSAND_EMPTYING, 1 },
1505 { EL_QUICKSAND_FAST_FILLING, 2 },
1506 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1507 { EL_MAGIC_WALL_FILLING, 2 },
1508 { EL_MAGIC_WALL_EMPTYING, 2 },
1509 { EL_BD_MAGIC_WALL_FILLING, 2 },
1510 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1511 { EL_DC_MAGIC_WALL_FILLING, 2 },
1512 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1514 { EL_UNDEFINED, 0 },
1522 collect_count_list[] =
1525 { EL_BD_DIAMOND, 1 },
1526 { EL_EMERALD_YELLOW, 1 },
1527 { EL_EMERALD_RED, 1 },
1528 { EL_EMERALD_PURPLE, 1 },
1530 { EL_SP_INFOTRON, 1 },
1534 { EL_UNDEFINED, 0 },
1542 access_direction_list[] =
1544 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1545 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1546 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1547 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1548 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1549 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1550 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1551 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1552 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1553 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1554 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1556 { EL_SP_PORT_LEFT, MV_RIGHT },
1557 { EL_SP_PORT_RIGHT, MV_LEFT },
1558 { EL_SP_PORT_UP, MV_DOWN },
1559 { EL_SP_PORT_DOWN, MV_UP },
1560 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1561 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1562 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1563 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1564 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1565 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1566 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1567 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1568 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1569 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1570 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1571 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1572 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1573 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1574 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1576 { EL_UNDEFINED, MV_NONE }
1579 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1581 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1582 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1583 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1584 IS_JUST_CHANGING(x, y))
1586 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1588 /* static variables for playfield scan mode (scanning forward or backward) */
1589 static int playfield_scan_start_x = 0;
1590 static int playfield_scan_start_y = 0;
1591 static int playfield_scan_delta_x = 1;
1592 static int playfield_scan_delta_y = 1;
1594 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1595 (y) >= 0 && (y) <= lev_fieldy - 1; \
1596 (y) += playfield_scan_delta_y) \
1597 for ((x) = playfield_scan_start_x; \
1598 (x) >= 0 && (x) <= lev_fieldx - 1; \
1599 (x) += playfield_scan_delta_x)
1602 void DEBUG_SetMaximumDynamite()
1606 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1607 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1608 local_player->inventory_element[local_player->inventory_size++] =
1613 static void InitPlayfieldScanModeVars()
1615 if (game.use_reverse_scan_direction)
1617 playfield_scan_start_x = lev_fieldx - 1;
1618 playfield_scan_start_y = lev_fieldy - 1;
1620 playfield_scan_delta_x = -1;
1621 playfield_scan_delta_y = -1;
1625 playfield_scan_start_x = 0;
1626 playfield_scan_start_y = 0;
1628 playfield_scan_delta_x = 1;
1629 playfield_scan_delta_y = 1;
1633 static void InitPlayfieldScanMode(int mode)
1635 game.use_reverse_scan_direction =
1636 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1638 InitPlayfieldScanModeVars();
1641 static int get_move_delay_from_stepsize(int move_stepsize)
1644 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1646 /* make sure that stepsize value is always a power of 2 */
1647 move_stepsize = (1 << log_2(move_stepsize));
1649 return TILEX / move_stepsize;
1652 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1655 int player_nr = player->index_nr;
1656 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1657 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1659 /* do no immediately change move delay -- the player might just be moving */
1660 player->move_delay_value_next = move_delay;
1662 /* information if player can move must be set separately */
1663 player->cannot_move = cannot_move;
1667 player->move_delay = game.initial_move_delay[player_nr];
1668 player->move_delay_value = game.initial_move_delay_value[player_nr];
1670 player->move_delay_value_next = -1;
1672 player->move_delay_reset_counter = 0;
1676 void GetPlayerConfig()
1678 GameFrameDelay = setup.game_frame_delay;
1680 if (!audio.sound_available)
1681 setup.sound_simple = FALSE;
1683 if (!audio.loops_available)
1684 setup.sound_loops = FALSE;
1686 if (!audio.music_available)
1687 setup.sound_music = FALSE;
1689 if (!video.fullscreen_available)
1690 setup.fullscreen = FALSE;
1692 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1694 SetAudioMode(setup.sound);
1698 int GetElementFromGroupElement(int element)
1700 if (IS_GROUP_ELEMENT(element))
1702 struct ElementGroupInfo *group = element_info[element].group;
1703 int last_anim_random_frame = gfx.anim_random_frame;
1706 if (group->choice_mode == ANIM_RANDOM)
1707 gfx.anim_random_frame = RND(group->num_elements_resolved);
1709 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1710 group->choice_mode, 0,
1713 if (group->choice_mode == ANIM_RANDOM)
1714 gfx.anim_random_frame = last_anim_random_frame;
1716 group->choice_pos++;
1718 element = group->element_resolved[element_pos];
1724 static void InitPlayerField(int x, int y, int element, boolean init_game)
1726 if (element == EL_SP_MURPHY)
1730 if (stored_player[0].present)
1732 Feld[x][y] = EL_SP_MURPHY_CLONE;
1738 stored_player[0].initial_element = element;
1739 stored_player[0].use_murphy = TRUE;
1741 if (!level.use_artwork_element[0])
1742 stored_player[0].artwork_element = EL_SP_MURPHY;
1745 Feld[x][y] = EL_PLAYER_1;
1751 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1752 int jx = player->jx, jy = player->jy;
1754 player->present = TRUE;
1756 player->block_last_field = (element == EL_SP_MURPHY ?
1757 level.sp_block_last_field :
1758 level.block_last_field);
1760 /* ---------- initialize player's last field block delay --------------- */
1762 /* always start with reliable default value (no adjustment needed) */
1763 player->block_delay_adjustment = 0;
1765 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1766 if (player->block_last_field && element == EL_SP_MURPHY)
1767 player->block_delay_adjustment = 1;
1769 /* special case 2: in game engines before 3.1.1, blocking was different */
1770 if (game.use_block_last_field_bug)
1771 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1773 if (!options.network || player->connected)
1775 player->active = TRUE;
1777 /* remove potentially duplicate players */
1778 if (StorePlayer[jx][jy] == Feld[x][y])
1779 StorePlayer[jx][jy] = 0;
1781 StorePlayer[x][y] = Feld[x][y];
1785 printf("Player %d activated.\n", player->element_nr);
1786 printf("[Local player is %d and currently %s.]\n",
1787 local_player->element_nr,
1788 local_player->active ? "active" : "not active");
1792 Feld[x][y] = EL_EMPTY;
1794 player->jx = player->last_jx = x;
1795 player->jy = player->last_jy = y;
1798 #if USE_PLAYER_REANIMATION
1801 int player_nr = GET_PLAYER_NR(element);
1802 struct PlayerInfo *player = &stored_player[player_nr];
1804 if (player->active && player->killed)
1805 player->reanimated = TRUE; /* if player was just killed, reanimate him */
1810 static void InitField(int x, int y, boolean init_game)
1812 int element = Feld[x][y];
1821 InitPlayerField(x, y, element, init_game);
1824 case EL_SOKOBAN_FIELD_PLAYER:
1825 element = Feld[x][y] = EL_PLAYER_1;
1826 InitField(x, y, init_game);
1828 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1829 InitField(x, y, init_game);
1832 case EL_SOKOBAN_FIELD_EMPTY:
1833 local_player->sokobanfields_still_needed++;
1837 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1838 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1839 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1840 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1841 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1842 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1843 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1844 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1845 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1846 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1855 case EL_SPACESHIP_RIGHT:
1856 case EL_SPACESHIP_UP:
1857 case EL_SPACESHIP_LEFT:
1858 case EL_SPACESHIP_DOWN:
1859 case EL_BD_BUTTERFLY:
1860 case EL_BD_BUTTERFLY_RIGHT:
1861 case EL_BD_BUTTERFLY_UP:
1862 case EL_BD_BUTTERFLY_LEFT:
1863 case EL_BD_BUTTERFLY_DOWN:
1865 case EL_BD_FIREFLY_RIGHT:
1866 case EL_BD_FIREFLY_UP:
1867 case EL_BD_FIREFLY_LEFT:
1868 case EL_BD_FIREFLY_DOWN:
1869 case EL_PACMAN_RIGHT:
1871 case EL_PACMAN_LEFT:
1872 case EL_PACMAN_DOWN:
1874 case EL_YAMYAM_LEFT:
1875 case EL_YAMYAM_RIGHT:
1877 case EL_YAMYAM_DOWN:
1878 case EL_DARK_YAMYAM:
1881 case EL_SP_SNIKSNAK:
1882 case EL_SP_ELECTRON:
1891 case EL_AMOEBA_FULL:
1896 case EL_AMOEBA_DROP:
1897 if (y == lev_fieldy - 1)
1899 Feld[x][y] = EL_AMOEBA_GROWING;
1900 Store[x][y] = EL_AMOEBA_WET;
1904 case EL_DYNAMITE_ACTIVE:
1905 case EL_SP_DISK_RED_ACTIVE:
1906 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1907 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1908 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1909 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1910 MovDelay[x][y] = 96;
1913 case EL_EM_DYNAMITE_ACTIVE:
1914 MovDelay[x][y] = 32;
1918 local_player->lights_still_needed++;
1922 local_player->friends_still_needed++;
1927 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1930 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1931 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1932 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1933 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1934 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1935 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1936 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1937 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1938 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1939 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1940 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1941 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1944 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1945 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1946 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1948 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1950 game.belt_dir[belt_nr] = belt_dir;
1951 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1953 else /* more than one switch -- set it like the first switch */
1955 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1960 #if !USE_BOTH_SWITCHGATE_SWITCHES
1961 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1963 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1966 case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1968 Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1972 case EL_LIGHT_SWITCH_ACTIVE:
1974 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1977 case EL_INVISIBLE_STEELWALL:
1978 case EL_INVISIBLE_WALL:
1979 case EL_INVISIBLE_SAND:
1980 if (game.light_time_left > 0 ||
1981 game.lenses_time_left > 0)
1982 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1985 case EL_EMC_MAGIC_BALL:
1986 if (game.ball_state)
1987 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1990 case EL_EMC_MAGIC_BALL_SWITCH:
1991 if (game.ball_state)
1992 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1995 case EL_TRIGGER_PLAYER:
1996 case EL_TRIGGER_ELEMENT:
1997 case EL_TRIGGER_CE_VALUE:
1998 case EL_TRIGGER_CE_SCORE:
2000 case EL_ANY_ELEMENT:
2001 case EL_CURRENT_CE_VALUE:
2002 case EL_CURRENT_CE_SCORE:
2019 /* reference elements should not be used on the playfield */
2020 Feld[x][y] = EL_EMPTY;
2024 if (IS_CUSTOM_ELEMENT(element))
2026 if (CAN_MOVE(element))
2029 #if USE_NEW_CUSTOM_VALUE
2030 if (!element_info[element].use_last_ce_value || init_game)
2031 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2034 else if (IS_GROUP_ELEMENT(element))
2036 Feld[x][y] = GetElementFromGroupElement(element);
2038 InitField(x, y, init_game);
2045 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2048 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2050 InitField(x, y, init_game);
2052 /* not needed to call InitMovDir() -- already done by InitField()! */
2053 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2054 CAN_MOVE(Feld[x][y]))
2058 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2060 int old_element = Feld[x][y];
2062 InitField(x, y, init_game);
2064 /* not needed to call InitMovDir() -- already done by InitField()! */
2065 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2066 CAN_MOVE(old_element) &&
2067 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2070 /* this case is in fact a combination of not less than three bugs:
2071 first, it calls InitMovDir() for elements that can move, although this is
2072 already done by InitField(); then, it checks the element that was at this
2073 field _before_ the call to InitField() (which can change it); lastly, it
2074 was not called for "mole with direction" elements, which were treated as
2075 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2081 static int get_key_element_from_nr(int key_nr)
2083 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2084 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2085 EL_EM_KEY_1 : EL_KEY_1);
2087 return key_base_element + key_nr;
2090 static int get_next_dropped_element(struct PlayerInfo *player)
2092 return (player->inventory_size > 0 ?
2093 player->inventory_element[player->inventory_size - 1] :
2094 player->inventory_infinite_element != EL_UNDEFINED ?
2095 player->inventory_infinite_element :
2096 player->dynabombs_left > 0 ?
2097 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2101 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2103 /* pos >= 0: get element from bottom of the stack;
2104 pos < 0: get element from top of the stack */
2108 int min_inventory_size = -pos;
2109 int inventory_pos = player->inventory_size - min_inventory_size;
2110 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2112 return (player->inventory_size >= min_inventory_size ?
2113 player->inventory_element[inventory_pos] :
2114 player->inventory_infinite_element != EL_UNDEFINED ?
2115 player->inventory_infinite_element :
2116 player->dynabombs_left >= min_dynabombs_left ?
2117 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2122 int min_dynabombs_left = pos + 1;
2123 int min_inventory_size = pos + 1 - player->dynabombs_left;
2124 int inventory_pos = pos - player->dynabombs_left;
2126 return (player->inventory_infinite_element != EL_UNDEFINED ?
2127 player->inventory_infinite_element :
2128 player->dynabombs_left >= min_dynabombs_left ?
2129 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2130 player->inventory_size >= min_inventory_size ?
2131 player->inventory_element[inventory_pos] :
2136 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2138 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2139 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2142 if (gpo1->sort_priority != gpo2->sort_priority)
2143 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2145 compare_result = gpo1->nr - gpo2->nr;
2147 return compare_result;
2150 void InitGameControlValues()
2154 for (i = 0; game_panel_controls[i].nr != -1; i++)
2156 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2157 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2158 struct TextPosInfo *pos = gpc->pos;
2160 int type = gpc->type;
2164 Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2165 Error(ERR_EXIT, "this should not happen -- please debug");
2168 /* force update of game controls after initialization */
2169 gpc->value = gpc->last_value = -1;
2170 gpc->frame = gpc->last_frame = -1;
2171 gpc->gfx_frame = -1;
2173 /* determine panel value width for later calculation of alignment */
2174 if (type == TYPE_INTEGER || type == TYPE_STRING)
2176 pos->width = pos->size * getFontWidth(pos->font);
2177 pos->height = getFontHeight(pos->font);
2179 else if (type == TYPE_ELEMENT)
2181 pos->width = pos->size;
2182 pos->height = pos->size;
2185 /* fill structure for game panel draw order */
2187 gpo->sort_priority = pos->sort_priority;
2190 /* sort game panel controls according to sort_priority and control number */
2191 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2192 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2195 void UpdatePlayfieldElementCount()
2197 boolean use_element_count = FALSE;
2200 /* first check if it is needed at all to calculate playfield element count */
2201 for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2202 if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2203 use_element_count = TRUE;
2205 if (!use_element_count)
2208 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2209 element_info[i].element_count = 0;
2211 SCAN_PLAYFIELD(x, y)
2213 element_info[Feld[x][y]].element_count++;
2216 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2217 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2218 if (IS_IN_GROUP(j, i))
2219 element_info[EL_GROUP_START + i].element_count +=
2220 element_info[j].element_count;
2223 void UpdateGameControlValues()
2226 int time = (local_player->LevelSolved ?
2227 local_player->LevelSolved_CountingTime :
2228 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2229 level.native_em_level->lev->time :
2230 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2231 level.native_sp_level->game_sp->time_played :
2232 level.time == 0 ? TimePlayed : TimeLeft);
2233 int score = (local_player->LevelSolved ?
2234 local_player->LevelSolved_CountingScore :
2235 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2236 level.native_em_level->lev->score :
2237 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2238 level.native_sp_level->game_sp->score :
2239 local_player->score);
2240 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2241 level.native_em_level->lev->required :
2242 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2243 level.native_sp_level->game_sp->infotrons_still_needed :
2244 local_player->gems_still_needed);
2245 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2246 level.native_em_level->lev->required > 0 :
2247 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2248 level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2249 local_player->gems_still_needed > 0 ||
2250 local_player->sokobanfields_still_needed > 0 ||
2251 local_player->lights_still_needed > 0);
2253 UpdatePlayfieldElementCount();
2255 /* update game panel control values */
2257 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2258 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2260 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2261 for (i = 0; i < MAX_NUM_KEYS; i++)
2262 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2263 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2264 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2266 if (game.centered_player_nr == -1)
2268 for (i = 0; i < MAX_PLAYERS; i++)
2270 /* only one player in Supaplex game engine */
2271 if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2274 for (k = 0; k < MAX_NUM_KEYS; k++)
2276 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2278 if (level.native_em_level->ply[i]->keys & (1 << k))
2279 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2280 get_key_element_from_nr(k);
2282 else if (stored_player[i].key[k])
2283 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2284 get_key_element_from_nr(k);
2287 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2288 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2289 level.native_em_level->ply[i]->dynamite;
2290 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2291 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2292 level.native_sp_level->game_sp->red_disk_count;
2294 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2295 stored_player[i].inventory_size;
2297 if (stored_player[i].num_white_keys > 0)
2298 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2301 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2302 stored_player[i].num_white_keys;
2307 int player_nr = game.centered_player_nr;
2309 for (k = 0; k < MAX_NUM_KEYS; k++)
2311 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2313 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2314 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2315 get_key_element_from_nr(k);
2317 else if (stored_player[player_nr].key[k])
2318 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2319 get_key_element_from_nr(k);
2322 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2323 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2324 level.native_em_level->ply[player_nr]->dynamite;
2325 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2326 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2327 level.native_sp_level->game_sp->red_disk_count;
2329 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2330 stored_player[player_nr].inventory_size;
2332 if (stored_player[player_nr].num_white_keys > 0)
2333 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2335 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2336 stored_player[player_nr].num_white_keys;
2339 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2341 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2342 get_inventory_element_from_pos(local_player, i);
2343 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2344 get_inventory_element_from_pos(local_player, -i - 1);
2347 game_panel_controls[GAME_PANEL_SCORE].value = score;
2348 game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2350 game_panel_controls[GAME_PANEL_TIME].value = time;
2352 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2353 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2354 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2356 game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2358 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2359 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2361 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2362 local_player->shield_normal_time_left;
2363 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2364 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2366 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2367 local_player->shield_deadly_time_left;
2369 game_panel_controls[GAME_PANEL_EXIT].value =
2370 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2372 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2373 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2374 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2375 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2376 EL_EMC_MAGIC_BALL_SWITCH);
2378 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2379 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2380 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2381 game.light_time_left;
2383 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2384 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2385 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2386 game.timegate_time_left;
2388 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2389 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2391 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2392 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2393 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2394 game.lenses_time_left;
2396 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2397 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2398 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2399 game.magnify_time_left;
2401 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2402 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2403 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2404 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2405 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2406 EL_BALLOON_SWITCH_NONE);
2408 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2409 local_player->dynabomb_count;
2410 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2411 local_player->dynabomb_size;
2412 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2413 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2415 game_panel_controls[GAME_PANEL_PENGUINS].value =
2416 local_player->friends_still_needed;
2418 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2419 local_player->sokobanfields_still_needed;
2420 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2421 local_player->sokobanfields_still_needed;
2423 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2424 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2426 for (i = 0; i < NUM_BELTS; i++)
2428 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2429 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2430 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2431 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2432 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2435 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2436 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2437 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2438 game.magic_wall_time_left;
2440 #if USE_PLAYER_GRAVITY
2441 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2442 local_player->gravity;
2444 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2447 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2448 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2450 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2451 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2452 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2453 game.panel.element[i].id : EL_UNDEFINED);
2455 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2456 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2457 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2458 element_info[game.panel.element_count[i].id].element_count : 0);
2460 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2461 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2462 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2463 element_info[game.panel.ce_score[i].id].collect_score : 0);
2465 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2466 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2467 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2468 element_info[game.panel.ce_score_element[i].id].collect_score :
2471 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2472 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2473 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2475 /* update game panel control frames */
2477 for (i = 0; game_panel_controls[i].nr != -1; i++)
2479 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2481 if (gpc->type == TYPE_ELEMENT)
2483 if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2485 int last_anim_random_frame = gfx.anim_random_frame;
2486 int element = gpc->value;
2487 int graphic = el2panelimg(element);
2489 if (gpc->value != gpc->last_value)
2492 gpc->gfx_random = INIT_GFX_RANDOM();
2498 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2499 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2500 gpc->gfx_random = INIT_GFX_RANDOM();
2503 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2504 gfx.anim_random_frame = gpc->gfx_random;
2506 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2507 gpc->gfx_frame = element_info[element].collect_score;
2509 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2512 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2513 gfx.anim_random_frame = last_anim_random_frame;
2519 void DisplayGameControlValues()
2521 boolean redraw_panel = FALSE;
2524 for (i = 0; game_panel_controls[i].nr != -1; i++)
2526 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2528 if (PANEL_DEACTIVATED(gpc->pos))
2531 if (gpc->value == gpc->last_value &&
2532 gpc->frame == gpc->last_frame)
2535 redraw_panel = TRUE;
2541 /* copy default game door content to main double buffer */
2542 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2543 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2545 /* redraw game control buttons */
2547 RedrawGameButtons();
2553 game_status = GAME_MODE_PSEUDO_PANEL;
2556 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2558 for (i = 0; game_panel_controls[i].nr != -1; i++)
2562 int nr = game_panel_order[i].nr;
2563 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2565 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2568 struct TextPosInfo *pos = gpc->pos;
2569 int type = gpc->type;
2570 int value = gpc->value;
2571 int frame = gpc->frame;
2573 int last_value = gpc->last_value;
2574 int last_frame = gpc->last_frame;
2576 int size = pos->size;
2577 int font = pos->font;
2578 boolean draw_masked = pos->draw_masked;
2579 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2581 if (PANEL_DEACTIVATED(pos))
2585 if (value == last_value && frame == last_frame)
2589 gpc->last_value = value;
2590 gpc->last_frame = frame;
2593 printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2596 if (type == TYPE_INTEGER)
2598 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2599 nr == GAME_PANEL_TIME)
2601 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2603 if (use_dynamic_size) /* use dynamic number of digits */
2605 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2606 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2607 int size2 = size1 + 1;
2608 int font1 = pos->font;
2609 int font2 = pos->font_alt;
2611 size = (value < value_change ? size1 : size2);
2612 font = (value < value_change ? font1 : font2);
2615 /* clear background if value just changed its size (dynamic digits) */
2616 if ((last_value < value_change) != (value < value_change))
2618 int width1 = size1 * getFontWidth(font1);
2619 int width2 = size2 * getFontWidth(font2);
2620 int max_width = MAX(width1, width2);
2621 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2623 pos->width = max_width;
2625 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2626 max_width, max_height);
2633 /* correct text size if "digits" is zero or less */
2635 size = strlen(int2str(value, size));
2637 /* dynamically correct text alignment */
2638 pos->width = size * getFontWidth(font);
2641 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2642 int2str(value, size), font, mask_mode);
2644 else if (type == TYPE_ELEMENT)
2646 int element, graphic;
2650 int dst_x = PANEL_XPOS(pos);
2651 int dst_y = PANEL_YPOS(pos);
2654 if (value != EL_UNDEFINED && value != EL_EMPTY)
2657 graphic = el2panelimg(value);
2659 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2662 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2666 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2669 width = graphic_info[graphic].width * size / TILESIZE;
2670 height = graphic_info[graphic].height * size / TILESIZE;
2674 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2675 dst_x - src_x, dst_y - src_y);
2676 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2681 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2686 if (value == EL_UNDEFINED || value == EL_EMPTY)
2688 element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2689 graphic = el2panelimg(element);
2691 src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2692 src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2693 src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2698 graphic = el2panelimg(value);
2700 getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2703 width = graphic_info[graphic].width * size / TILESIZE;
2704 height = graphic_info[graphic].height * size / TILESIZE;
2706 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2709 else if (type == TYPE_STRING)
2711 boolean active = (value != 0);
2712 char *state_normal = "off";
2713 char *state_active = "on";
2714 char *state = (active ? state_active : state_normal);
2715 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2716 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2717 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2718 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2720 if (nr == GAME_PANEL_GRAVITY_STATE)
2722 int font1 = pos->font; /* (used for normal state) */
2723 int font2 = pos->font_alt; /* (used for active state) */
2725 int size1 = strlen(state_normal);
2726 int size2 = strlen(state_active);
2727 int width1 = size1 * getFontWidth(font1);
2728 int width2 = size2 * getFontWidth(font2);
2729 int max_width = MAX(width1, width2);
2730 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2732 pos->width = max_width;
2734 /* clear background for values that may have changed its size */
2735 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2736 max_width, max_height);
2739 font = (active ? font2 : font1);
2749 /* don't truncate output if "chars" is zero or less */
2752 /* dynamically correct text alignment */
2753 pos->width = size * getFontWidth(font);
2757 s_cut = getStringCopyN(s, size);
2759 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2760 s_cut, font, mask_mode);
2766 redraw_mask |= REDRAW_DOOR_1;
2769 game_status = GAME_MODE_PLAYING;
2772 void UpdateAndDisplayGameControlValues()
2774 if (tape.warp_forward)
2777 UpdateGameControlValues();
2778 DisplayGameControlValues();
2781 void DrawGameValue_Emeralds(int value)
2783 struct TextPosInfo *pos = &game.panel.gems;
2785 int font_nr = pos->font;
2787 int font_nr = FONT_TEXT_2;
2789 int font_width = getFontWidth(font_nr);
2790 int chars = pos->size;
2793 return; /* !!! USE NEW STUFF !!! */
2796 if (PANEL_DEACTIVATED(pos))
2799 pos->width = chars * font_width;
2801 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2804 void DrawGameValue_Dynamite(int value)
2806 struct TextPosInfo *pos = &game.panel.inventory_count;
2808 int font_nr = pos->font;
2810 int font_nr = FONT_TEXT_2;
2812 int font_width = getFontWidth(font_nr);
2813 int chars = pos->size;
2816 return; /* !!! USE NEW STUFF !!! */
2819 if (PANEL_DEACTIVATED(pos))
2822 pos->width = chars * font_width;
2824 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2827 void DrawGameValue_Score(int value)
2829 struct TextPosInfo *pos = &game.panel.score;
2831 int font_nr = pos->font;
2833 int font_nr = FONT_TEXT_2;
2835 int font_width = getFontWidth(font_nr);
2836 int chars = pos->size;
2839 return; /* !!! USE NEW STUFF !!! */
2842 if (PANEL_DEACTIVATED(pos))
2845 pos->width = chars * font_width;
2847 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2850 void DrawGameValue_Time(int value)
2852 struct TextPosInfo *pos = &game.panel.time;
2853 static int last_value = -1;
2856 int chars = pos->size;
2858 int font1_nr = pos->font;
2859 int font2_nr = pos->font_alt;
2861 int font1_nr = FONT_TEXT_2;
2862 int font2_nr = FONT_TEXT_1;
2864 int font_nr = font1_nr;
2865 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2868 return; /* !!! USE NEW STUFF !!! */
2871 if (PANEL_DEACTIVATED(pos))
2874 if (use_dynamic_chars) /* use dynamic number of chars */
2876 chars = (value < 1000 ? chars1 : chars2);
2877 font_nr = (value < 1000 ? font1_nr : font2_nr);
2880 /* clear background if value just changed its size (dynamic chars only) */
2881 if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2883 int width1 = chars1 * getFontWidth(font1_nr);
2884 int width2 = chars2 * getFontWidth(font2_nr);
2885 int max_width = MAX(width1, width2);
2886 int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2888 pos->width = max_width;
2890 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2891 max_width, max_height);
2894 pos->width = chars * getFontWidth(font_nr);
2896 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2901 void DrawGameValue_Level(int value)
2903 struct TextPosInfo *pos = &game.panel.level_number;
2906 int chars = pos->size;
2908 int font1_nr = pos->font;
2909 int font2_nr = pos->font_alt;
2911 int font1_nr = FONT_TEXT_2;
2912 int font2_nr = FONT_TEXT_1;
2914 int font_nr = font1_nr;
2915 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2918 return; /* !!! USE NEW STUFF !!! */
2921 if (PANEL_DEACTIVATED(pos))
2924 if (use_dynamic_chars) /* use dynamic number of chars */
2926 chars = (level_nr < 100 ? chars1 : chars2);
2927 font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2930 pos->width = chars * getFontWidth(font_nr);
2932 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2935 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2938 struct TextPosInfo *pos = &game.panel.keys;
2941 int base_key_graphic = EL_KEY_1;
2946 return; /* !!! USE NEW STUFF !!! */
2950 if (PANEL_DEACTIVATED(pos))
2955 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2956 base_key_graphic = EL_EM_KEY_1;
2960 pos->width = 4 * MINI_TILEX;
2964 for (i = 0; i < MAX_NUM_KEYS; i++)
2966 /* currently only 4 of 8 possible keys are displayed */
2967 for (i = 0; i < STD_NUM_KEYS; i++)
2971 struct TextPosInfo *pos = &game.panel.key[i];
2973 int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2974 int src_y = DOOR_GFX_PAGEY1 + 123;
2976 int dst_x = PANEL_XPOS(pos);
2977 int dst_y = PANEL_YPOS(pos);
2979 int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2980 int dst_y = PANEL_YPOS(pos);
2984 int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2985 level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2987 int graphic = el2edimg(element);
2991 if (PANEL_DEACTIVATED(pos))
2996 /* masked blit with tiles from half-size scaled bitmap does not work yet
2997 (no mask bitmap created for these sizes after loading and scaling) --
2998 solution: load without creating mask, scale, then create final mask */
3000 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3001 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3006 int graphic = el2edimg(base_key_graphic + i);
3011 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
3013 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
3014 dst_x - src_x, dst_y - src_y);
3015 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
3021 DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
3023 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3024 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3027 DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
3029 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3030 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3038 void DrawGameValue_Emeralds(int value)
3040 int font_nr = FONT_TEXT_2;
3041 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3043 if (PANEL_DEACTIVATED(game.panel.gems))
3046 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
3049 void DrawGameValue_Dynamite(int value)
3051 int font_nr = FONT_TEXT_2;
3052 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3054 if (PANEL_DEACTIVATED(game.panel.inventory_count))
3057 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
3060 void DrawGameValue_Score(int value)
3062 int font_nr = FONT_TEXT_2;
3063 int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
3065 if (PANEL_DEACTIVATED(game.panel.score))
3068 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
3071 void DrawGameValue_Time(int value)
3073 int font1_nr = FONT_TEXT_2;
3075 int font2_nr = FONT_TEXT_1;
3077 int font2_nr = FONT_LEVEL_NUMBER;
3079 int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
3080 int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
3082 if (PANEL_DEACTIVATED(game.panel.time))
3085 /* clear background if value just changed its size */
3086 if (value == 999 || value == 1000)
3087 ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
3090 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
3092 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
3095 void DrawGameValue_Level(int value)
3097 int font1_nr = FONT_TEXT_2;
3099 int font2_nr = FONT_TEXT_1;
3101 int font2_nr = FONT_LEVEL_NUMBER;
3104 if (PANEL_DEACTIVATED(game.panel.level))
3108 DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
3110 DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
3113 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
3115 int base_key_graphic = EL_KEY_1;
3118 if (PANEL_DEACTIVATED(game.panel.keys))
3121 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3122 base_key_graphic = EL_EM_KEY_1;
3124 /* currently only 4 of 8 possible keys are displayed */
3125 for (i = 0; i < STD_NUM_KEYS; i++)
3127 int x = XX_KEYS + i * MINI_TILEX;
3131 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3133 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3134 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3140 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3143 int key[MAX_NUM_KEYS];
3146 /* prevent EM engine from updating time/score values parallel to GameWon() */
3147 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3148 local_player->LevelSolved)
3151 for (i = 0; i < MAX_NUM_KEYS; i++)
3152 key[i] = key_bits & (1 << i);
3154 DrawGameValue_Level(level_nr);
3156 DrawGameValue_Emeralds(emeralds);
3157 DrawGameValue_Dynamite(dynamite);
3158 DrawGameValue_Score(score);
3159 DrawGameValue_Time(time);
3161 DrawGameValue_Keys(key);
3164 void UpdateGameDoorValues()
3166 UpdateGameControlValues();
3169 void DrawGameDoorValues()
3171 DisplayGameControlValues();
3174 void DrawGameDoorValues_OLD()
3176 int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
3177 int dynamite_value = 0;
3178 int score_value = (local_player->LevelSolved ? local_player->score_final :
3179 local_player->score);
3180 int gems_value = local_player->gems_still_needed;
3184 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3186 DrawGameDoorValues_EM();
3191 if (game.centered_player_nr == -1)
3193 for (i = 0; i < MAX_PLAYERS; i++)
3195 for (j = 0; j < MAX_NUM_KEYS; j++)
3196 if (stored_player[i].key[j])
3197 key_bits |= (1 << j);
3199 dynamite_value += stored_player[i].inventory_size;
3204 int player_nr = game.centered_player_nr;
3206 for (i = 0; i < MAX_NUM_KEYS; i++)
3207 if (stored_player[player_nr].key[i])
3208 key_bits |= (1 << i);
3210 dynamite_value = stored_player[player_nr].inventory_size;
3213 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3219 =============================================================================
3221 -----------------------------------------------------------------------------
3222 initialize game engine due to level / tape version number
3223 =============================================================================
3226 static void InitGameEngine()
3228 int i, j, k, l, x, y;
3230 /* set game engine from tape file when re-playing, else from level file */
3231 game.engine_version = (tape.playing ? tape.engine_version :
3232 level.game_version);
3234 /* ---------------------------------------------------------------------- */
3235 /* set flags for bugs and changes according to active game engine version */
3236 /* ---------------------------------------------------------------------- */
3239 Summary of bugfix/change:
3240 Fixed handling for custom elements that change when pushed by the player.
3242 Fixed/changed in version:
3246 Before 3.1.0, custom elements that "change when pushing" changed directly
3247 after the player started pushing them (until then handled in "DigField()").
3248 Since 3.1.0, these custom elements are not changed until the "pushing"
3249 move of the element is finished (now handled in "ContinueMoving()").
3251 Affected levels/tapes:
3252 The first condition is generally needed for all levels/tapes before version
3253 3.1.0, which might use the old behaviour before it was changed; known tapes
3254 that are affected are some tapes from the level set "Walpurgis Gardens" by
3256 The second condition is an exception from the above case and is needed for
3257 the special case of tapes recorded with game (not engine!) version 3.1.0 or
3258 above (including some development versions of 3.1.0), but before it was
3259 known that this change would break tapes like the above and was fixed in
3260 3.1.1, so that the changed behaviour was active although the engine version
3261 while recording maybe was before 3.1.0. There is at least one tape that is
3262 affected by this exception, which is the tape for the one-level set "Bug
3263 Machine" by Juergen Bonhagen.
3266 game.use_change_when_pushing_bug =
3267 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3269 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3270 tape.game_version < VERSION_IDENT(3,1,1,0)));
3273 Summary of bugfix/change:
3274 Fixed handling for blocking the field the player leaves when moving.
3276 Fixed/changed in version:
3280 Before 3.1.1, when "block last field when moving" was enabled, the field
3281 the player is leaving when moving was blocked for the time of the move,
3282 and was directly unblocked afterwards. This resulted in the last field
3283 being blocked for exactly one less than the number of frames of one player
3284 move. Additionally, even when blocking was disabled, the last field was
3285 blocked for exactly one frame.
3286 Since 3.1.1, due to changes in player movement handling, the last field
3287 is not blocked at all when blocking is disabled. When blocking is enabled,
3288 the last field is blocked for exactly the number of frames of one player
3289 move. Additionally, if the player is Murphy, the hero of Supaplex, the
3290 last field is blocked for exactly one more than the number of frames of
3293 Affected levels/tapes:
3294 (!!! yet to be determined -- probably many !!!)
3297 game.use_block_last_field_bug =
3298 (game.engine_version < VERSION_IDENT(3,1,1,0));
3301 Summary of bugfix/change:
3302 Changed behaviour of CE changes with multiple changes per single frame.
3304 Fixed/changed in version:
3308 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3309 This resulted in race conditions where CEs seem to behave strange in some
3310 situations (where triggered CE changes were just skipped because there was
3311 already a CE change on that tile in the playfield in that engine frame).
3312 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3313 (The number of changes per frame must be limited in any case, because else
3314 it is easily possible to define CE changes that would result in an infinite
3315 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3316 should be set large enough so that it would only be reached in cases where
3317 the corresponding CE change conditions run into a loop. Therefore, it seems
3318 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3319 maximal number of change pages for custom elements.)
3321 Affected levels/tapes:
3325 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3326 game.max_num_changes_per_frame = 1;
3328 game.max_num_changes_per_frame =
3329 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3332 /* ---------------------------------------------------------------------- */
3334 /* default scan direction: scan playfield from top/left to bottom/right */
3335 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3337 /* dynamically adjust element properties according to game engine version */
3338 InitElementPropertiesEngine(game.engine_version);
3341 printf("level %d: level version == %06d\n", level_nr, level.game_version);
3342 printf(" tape version == %06d [%s] [file: %06d]\n",
3343 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3345 printf(" => game.engine_version == %06d\n", game.engine_version);
3348 /* ---------- initialize player's initial move delay --------------------- */
3350 /* dynamically adjust player properties according to level information */
3351 for (i = 0; i < MAX_PLAYERS; i++)
3352 game.initial_move_delay_value[i] =
3353 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3355 /* dynamically adjust player properties according to game engine version */
3356 for (i = 0; i < MAX_PLAYERS; i++)
3357 game.initial_move_delay[i] =
3358 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3359 game.initial_move_delay_value[i] : 0);
3361 /* ---------- initialize player's initial push delay --------------------- */
3363 /* dynamically adjust player properties according to game engine version */
3364 game.initial_push_delay_value =
3365 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3367 /* ---------- initialize changing elements ------------------------------- */
3369 /* initialize changing elements information */
3370 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3372 struct ElementInfo *ei = &element_info[i];
3374 /* this pointer might have been changed in the level editor */
3375 ei->change = &ei->change_page[0];
3377 if (!IS_CUSTOM_ELEMENT(i))
3379 ei->change->target_element = EL_EMPTY_SPACE;
3380 ei->change->delay_fixed = 0;
3381 ei->change->delay_random = 0;
3382 ei->change->delay_frames = 1;
3385 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3387 ei->has_change_event[j] = FALSE;
3389 ei->event_page_nr[j] = 0;
3390 ei->event_page[j] = &ei->change_page[0];
3394 /* add changing elements from pre-defined list */
3395 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3397 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3398 struct ElementInfo *ei = &element_info[ch_delay->element];
3400 ei->change->target_element = ch_delay->target_element;
3401 ei->change->delay_fixed = ch_delay->change_delay;
3403 ei->change->pre_change_function = ch_delay->pre_change_function;
3404 ei->change->change_function = ch_delay->change_function;
3405 ei->change->post_change_function = ch_delay->post_change_function;
3407 ei->change->can_change = TRUE;
3408 ei->change->can_change_or_has_action = TRUE;
3410 ei->has_change_event[CE_DELAY] = TRUE;
3412 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3413 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3416 /* ---------- initialize internal run-time variables --------------------- */
3418 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3420 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3422 for (j = 0; j < ei->num_change_pages; j++)
3424 ei->change_page[j].can_change_or_has_action =
3425 (ei->change_page[j].can_change |
3426 ei->change_page[j].has_action);
3430 /* add change events from custom element configuration */
3431 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3433 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3435 for (j = 0; j < ei->num_change_pages; j++)
3437 if (!ei->change_page[j].can_change_or_has_action)
3440 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3442 /* only add event page for the first page found with this event */
3443 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3445 ei->has_change_event[k] = TRUE;
3447 ei->event_page_nr[k] = j;
3448 ei->event_page[k] = &ei->change_page[j];
3455 /* ---------- initialize reference elements in change conditions --------- */
3457 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3459 int element = EL_CUSTOM_START + i;
3460 struct ElementInfo *ei = &element_info[element];
3462 for (j = 0; j < ei->num_change_pages; j++)
3464 int trigger_element = ei->change_page[j].initial_trigger_element;
3466 if (trigger_element >= EL_PREV_CE_8 &&
3467 trigger_element <= EL_NEXT_CE_8)
3468 trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3470 ei->change_page[j].trigger_element = trigger_element;
3475 /* ---------- initialize run-time trigger player and element ------------- */
3477 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3479 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3481 for (j = 0; j < ei->num_change_pages; j++)
3483 ei->change_page[j].actual_trigger_element = EL_EMPTY;
3484 ei->change_page[j].actual_trigger_player = EL_EMPTY;
3485 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3486 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3487 ei->change_page[j].actual_trigger_ce_value = 0;
3488 ei->change_page[j].actual_trigger_ce_score = 0;
3492 /* ---------- initialize trigger events ---------------------------------- */
3494 /* initialize trigger events information */
3495 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3496 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3497 trigger_events[i][j] = FALSE;
3499 /* add trigger events from element change event properties */
3500 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3502 struct ElementInfo *ei = &element_info[i];
3504 for (j = 0; j < ei->num_change_pages; j++)
3506 if (!ei->change_page[j].can_change_or_has_action)
3509 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3511 int trigger_element = ei->change_page[j].trigger_element;
3513 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3515 if (ei->change_page[j].has_event[k])
3517 if (IS_GROUP_ELEMENT(trigger_element))
3519 struct ElementGroupInfo *group =
3520 element_info[trigger_element].group;
3522 for (l = 0; l < group->num_elements_resolved; l++)
3523 trigger_events[group->element_resolved[l]][k] = TRUE;
3525 else if (trigger_element == EL_ANY_ELEMENT)
3526 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3527 trigger_events[l][k] = TRUE;
3529 trigger_events[trigger_element][k] = TRUE;
3536 /* ---------- initialize push delay -------------------------------------- */
3538 /* initialize push delay values to default */
3539 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3541 if (!IS_CUSTOM_ELEMENT(i))
3543 /* set default push delay values (corrected since version 3.0.7-1) */
3544 if (game.engine_version < VERSION_IDENT(3,0,7,1))
3546 element_info[i].push_delay_fixed = 2;
3547 element_info[i].push_delay_random = 8;
3551 element_info[i].push_delay_fixed = 8;
3552 element_info[i].push_delay_random = 8;
3557 /* set push delay value for certain elements from pre-defined list */
3558 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3560 int e = push_delay_list[i].element;
3562 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
3563 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3566 /* set push delay value for Supaplex elements for newer engine versions */
3567 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3569 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3571 if (IS_SP_ELEMENT(i))
3573 /* set SP push delay to just enough to push under a falling zonk */
3574 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3576 element_info[i].push_delay_fixed = delay;
3577 element_info[i].push_delay_random = 0;
3582 /* ---------- initialize move stepsize ----------------------------------- */
3584 /* initialize move stepsize values to default */
3585 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3586 if (!IS_CUSTOM_ELEMENT(i))
3587 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3589 /* set move stepsize value for certain elements from pre-defined list */
3590 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3592 int e = move_stepsize_list[i].element;
3594 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3597 /* ---------- initialize collect score ----------------------------------- */
3599 /* initialize collect score values for custom elements from initial value */
3600 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3601 if (IS_CUSTOM_ELEMENT(i))
3602 element_info[i].collect_score = element_info[i].collect_score_initial;
3604 /* ---------- initialize collect count ----------------------------------- */
3606 /* initialize collect count values for non-custom elements */
3607 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3608 if (!IS_CUSTOM_ELEMENT(i))
3609 element_info[i].collect_count_initial = 0;
3611 /* add collect count values for all elements from pre-defined list */
3612 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3613 element_info[collect_count_list[i].element].collect_count_initial =
3614 collect_count_list[i].count;
3616 /* ---------- initialize access direction -------------------------------- */
3618 /* initialize access direction values to default (access from every side) */
3619 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3620 if (!IS_CUSTOM_ELEMENT(i))
3621 element_info[i].access_direction = MV_ALL_DIRECTIONS;
3623 /* set access direction value for certain elements from pre-defined list */
3624 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3625 element_info[access_direction_list[i].element].access_direction =
3626 access_direction_list[i].direction;
3628 /* ---------- initialize explosion content ------------------------------- */
3629 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3631 if (IS_CUSTOM_ELEMENT(i))
3634 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3636 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3638 element_info[i].content.e[x][y] =
3639 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3640 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3641 i == EL_PLAYER_3 ? EL_EMERALD :
3642 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3643 i == EL_MOLE ? EL_EMERALD_RED :
3644 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3645 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3646 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3647 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3648 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3649 i == EL_WALL_EMERALD ? EL_EMERALD :
3650 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3651 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3652 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3653 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3654 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3655 i == EL_WALL_PEARL ? EL_PEARL :
3656 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3661 /* ---------- initialize recursion detection ------------------------------ */
3662 recursion_loop_depth = 0;
3663 recursion_loop_detected = FALSE;
3664 recursion_loop_element = EL_UNDEFINED;
3666 /* ---------- initialize graphics engine ---------------------------------- */
3667 game.scroll_delay_value =
3668 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3669 setup.scroll_delay ? setup.scroll_delay_value : 0);
3670 game.scroll_delay_value =
3671 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3674 int get_num_special_action(int element, int action_first, int action_last)
3676 int num_special_action = 0;
3679 for (i = action_first; i <= action_last; i++)
3681 boolean found = FALSE;
3683 for (j = 0; j < NUM_DIRECTIONS; j++)
3684 if (el_act_dir2img(element, i, j) !=
3685 el_act_dir2img(element, ACTION_DEFAULT, j))
3689 num_special_action++;
3694 return num_special_action;
3699 =============================================================================
3701 -----------------------------------------------------------------------------
3702 initialize and start new game
3703 =============================================================================
3708 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3709 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3710 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3712 boolean do_fading = (game_status == GAME_MODE_MAIN);
3715 int initial_move_dir = MV_DOWN;
3717 int initial_move_dir = MV_NONE;
3721 game_status = GAME_MODE_PLAYING;
3724 InitGameControlValues();
3726 /* don't play tapes over network */
3727 network_playing = (options.network && !tape.playing);
3729 for (i = 0; i < MAX_PLAYERS; i++)
3731 struct PlayerInfo *player = &stored_player[i];
3733 player->index_nr = i;
3734 player->index_bit = (1 << i);
3735 player->element_nr = EL_PLAYER_1 + i;
3737 player->present = FALSE;
3738 player->active = FALSE;
3739 player->mapped = FALSE;
3741 player->killed = FALSE;
3742 player->reanimated = FALSE;
3745 player->effective_action = 0;
3746 player->programmed_action = 0;
3749 player->score_final = 0;
3751 player->gems_still_needed = level.gems_needed;
3752 player->sokobanfields_still_needed = 0;
3753 player->lights_still_needed = 0;
3754 player->friends_still_needed = 0;
3756 for (j = 0; j < MAX_NUM_KEYS; j++)
3757 player->key[j] = FALSE;
3759 player->num_white_keys = 0;
3761 player->dynabomb_count = 0;
3762 player->dynabomb_size = 1;
3763 player->dynabombs_left = 0;
3764 player->dynabomb_xl = FALSE;
3766 player->MovDir = initial_move_dir;
3769 player->GfxDir = initial_move_dir;
3770 player->GfxAction = ACTION_DEFAULT;
3772 player->StepFrame = 0;
3774 player->initial_element = player->element_nr;
3775 player->artwork_element =
3776 (level.use_artwork_element[i] ? level.artwork_element[i] :
3777 player->element_nr);
3778 player->use_murphy = FALSE;
3780 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3781 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3783 player->gravity = level.initial_player_gravity[i];
3785 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3787 player->actual_frame_counter = 0;
3789 player->step_counter = 0;
3791 player->last_move_dir = initial_move_dir;
3793 player->is_active = FALSE;
3795 player->is_waiting = FALSE;
3796 player->is_moving = FALSE;
3797 player->is_auto_moving = FALSE;
3798 player->is_digging = FALSE;
3799 player->is_snapping = FALSE;
3800 player->is_collecting = FALSE;
3801 player->is_pushing = FALSE;
3802 player->is_switching = FALSE;
3803 player->is_dropping = FALSE;
3804 player->is_dropping_pressed = FALSE;
3806 player->is_bored = FALSE;
3807 player->is_sleeping = FALSE;
3809 player->frame_counter_bored = -1;
3810 player->frame_counter_sleeping = -1;
3812 player->anim_delay_counter = 0;
3813 player->post_delay_counter = 0;
3815 player->dir_waiting = initial_move_dir;
3816 player->action_waiting = ACTION_DEFAULT;
3817 player->last_action_waiting = ACTION_DEFAULT;
3818 player->special_action_bored = ACTION_DEFAULT;
3819 player->special_action_sleeping = ACTION_DEFAULT;
3821 player->switch_x = -1;
3822 player->switch_y = -1;
3824 player->drop_x = -1;
3825 player->drop_y = -1;
3827 player->show_envelope = 0;
3829 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3831 player->push_delay = -1; /* initialized when pushing starts */
3832 player->push_delay_value = game.initial_push_delay_value;
3834 player->drop_delay = 0;
3835 player->drop_pressed_delay = 0;
3837 player->last_jx = -1;
3838 player->last_jy = -1;
3842 player->shield_normal_time_left = 0;
3843 player->shield_deadly_time_left = 0;
3845 player->inventory_infinite_element = EL_UNDEFINED;
3846 player->inventory_size = 0;
3848 if (level.use_initial_inventory[i])
3850 for (j = 0; j < level.initial_inventory_size[i]; j++)
3852 int element = level.initial_inventory_content[i][j];
3853 int collect_count = element_info[element].collect_count_initial;
3856 if (!IS_CUSTOM_ELEMENT(element))
3859 if (collect_count == 0)
3860 player->inventory_infinite_element = element;
3862 for (k = 0; k < collect_count; k++)
3863 if (player->inventory_size < MAX_INVENTORY_SIZE)
3864 player->inventory_element[player->inventory_size++] = element;
3868 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3869 SnapField(player, 0, 0);
3871 player->LevelSolved = FALSE;
3872 player->GameOver = FALSE;
3874 player->LevelSolved_GameWon = FALSE;
3875 player->LevelSolved_GameEnd = FALSE;
3876 player->LevelSolved_PanelOff = FALSE;
3877 player->LevelSolved_SaveTape = FALSE;
3878 player->LevelSolved_SaveScore = FALSE;
3879 player->LevelSolved_CountingTime = 0;
3880 player->LevelSolved_CountingScore = 0;
3882 map_player_action[i] = i;
3885 network_player_action_received = FALSE;
3887 #if defined(NETWORK_AVALIABLE)
3888 /* initial null action */
3889 if (network_playing)
3890 SendToServer_MovePlayer(MV_NONE);
3899 TimeLeft = level.time;
3902 ScreenMovDir = MV_NONE;
3906 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3908 AllPlayersGone = FALSE;
3910 game.yamyam_content_nr = 0;
3911 game.robot_wheel_active = FALSE;
3912 game.magic_wall_active = FALSE;
3913 game.magic_wall_time_left = 0;
3914 game.light_time_left = 0;
3915 game.timegate_time_left = 0;
3916 game.switchgate_pos = 0;
3917 game.wind_direction = level.wind_direction_initial;
3919 #if !USE_PLAYER_GRAVITY
3920 game.gravity = FALSE;
3921 game.explosions_delayed = TRUE;
3924 game.lenses_time_left = 0;
3925 game.magnify_time_left = 0;
3927 game.ball_state = level.ball_state_initial;
3928 game.ball_content_nr = 0;
3930 game.envelope_active = FALSE;
3932 /* set focus to local player for network games, else to all players */
3933 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3934 game.centered_player_nr_next = game.centered_player_nr;
3935 game.set_centered_player = FALSE;
3937 if (network_playing && tape.recording)
3939 /* store client dependent player focus when recording network games */
3940 tape.centered_player_nr_next = game.centered_player_nr_next;
3941 tape.set_centered_player = TRUE;
3944 for (i = 0; i < NUM_BELTS; i++)
3946 game.belt_dir[i] = MV_NONE;
3947 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3950 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3951 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3953 SCAN_PLAYFIELD(x, y)
3955 Feld[x][y] = level.field[x][y];
3956 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3957 ChangeDelay[x][y] = 0;
3958 ChangePage[x][y] = -1;
3959 #if USE_NEW_CUSTOM_VALUE
3960 CustomValue[x][y] = 0; /* initialized in InitField() */
3962 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3964 WasJustMoving[x][y] = 0;
3965 WasJustFalling[x][y] = 0;
3966 CheckCollision[x][y] = 0;
3967 CheckImpact[x][y] = 0;
3969 Pushed[x][y] = FALSE;
3971 ChangeCount[x][y] = 0;
3972 ChangeEvent[x][y] = -1;
3974 ExplodePhase[x][y] = 0;
3975 ExplodeDelay[x][y] = 0;
3976 ExplodeField[x][y] = EX_TYPE_NONE;
3978 RunnerVisit[x][y] = 0;
3979 PlayerVisit[x][y] = 0;
3982 GfxRandom[x][y] = INIT_GFX_RANDOM();
3983 GfxElement[x][y] = EL_UNDEFINED;
3984 GfxAction[x][y] = ACTION_DEFAULT;
3985 GfxDir[x][y] = MV_NONE;
3986 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3989 SCAN_PLAYFIELD(x, y)
3991 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3993 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3995 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3998 InitField(x, y, TRUE);
4000 ResetGfxAnimation(x, y);
4005 for (i = 0; i < MAX_PLAYERS; i++)
4007 struct PlayerInfo *player = &stored_player[i];
4009 /* set number of special actions for bored and sleeping animation */
4010 player->num_special_action_bored =
4011 get_num_special_action(player->artwork_element,
4012 ACTION_BORING_1, ACTION_BORING_LAST);
4013 player->num_special_action_sleeping =
4014 get_num_special_action(player->artwork_element,
4015 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
4018 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
4019 emulate_sb ? EMU_SOKOBAN :
4020 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
4022 #if USE_NEW_ALL_SLIPPERY
4023 /* initialize type of slippery elements */
4024 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4026 if (!IS_CUSTOM_ELEMENT(i))
4028 /* default: elements slip down either to the left or right randomly */
4029 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
4031 /* SP style elements prefer to slip down on the left side */
4032 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
4033 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4035 /* BD style elements prefer to slip down on the left side */
4036 if (game.emulation == EMU_BOULDERDASH)
4037 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4042 /* initialize explosion and ignition delay */
4043 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4045 if (!IS_CUSTOM_ELEMENT(i))
4048 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4049 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4050 game.emulation == EMU_SUPAPLEX ? 3 : 2);
4051 int last_phase = (num_phase + 1) * delay;
4052 int half_phase = (num_phase / 2) * delay;
4054 element_info[i].explosion_delay = last_phase - 1;
4055 element_info[i].ignition_delay = half_phase;
4057 if (i == EL_BLACK_ORB)
4058 element_info[i].ignition_delay = 1;
4062 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
4063 element_info[i].explosion_delay = 1;
4065 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
4066 element_info[i].ignition_delay = 1;
4070 /* correct non-moving belts to start moving left */
4071 for (i = 0; i < NUM_BELTS; i++)
4072 if (game.belt_dir[i] == MV_NONE)
4073 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
4075 #if USE_NEW_PLAYER_ASSIGNMENTS
4076 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4077 /* choose default local player */
4078 local_player = &stored_player[0];
4080 for (i = 0; i < MAX_PLAYERS; i++)
4081 stored_player[i].connected = FALSE;
4083 local_player->connected = TRUE;
4084 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4088 /* try to guess locally connected team mode players (needed for correct
4089 assignment of player figures from level to locally playing players) */
4091 for (i = 0; i < MAX_PLAYERS; i++)
4092 if (tape.player_participates[i])
4093 stored_player[i].connected = TRUE;
4095 else if (setup.team_mode && !options.network)
4097 /* try to guess locally connected team mode players (needed for correct
4098 assignment of player figures from level to locally playing players) */
4100 for (i = 0; i < MAX_PLAYERS; i++)
4101 if (setup.input[i].use_joystick ||
4102 setup.input[i].key.left != KSYM_UNDEFINED)
4103 stored_player[i].connected = TRUE;
4107 for (i = 0; i < MAX_PLAYERS; i++)
4108 printf("::: player %d: %s\n", i,
4109 (stored_player[i].connected ? "connected" : "not connected"));
4111 for (i = 0; i < MAX_PLAYERS; i++)
4112 printf("::: player %d: %s\n", i,
4113 (stored_player[i].present ? "present" : "not present"));
4116 /* check if any connected player was not found in playfield */
4117 for (i = 0; i < MAX_PLAYERS; i++)
4119 struct PlayerInfo *player = &stored_player[i];
4121 if (player->connected && !player->present)
4123 struct PlayerInfo *field_player = NULL;
4126 printf("::: looking for field player for player %d ...\n", i);
4129 /* assign first free player found that is present in the playfield */
4131 /* first try: look for unmapped playfield player that is not connected */
4132 if (field_player == NULL)
4133 for (j = 0; j < MAX_PLAYERS; j++)
4134 if (stored_player[j].present &&
4135 !stored_player[j].mapped &&
4136 !stored_player[j].connected)
4137 field_player = &stored_player[j];
4139 /* second try: look for *any* unmapped playfield player */
4140 if (field_player == NULL)
4141 for (j = 0; j < MAX_PLAYERS; j++)
4142 if (stored_player[j].present &&
4143 !stored_player[j].mapped)
4144 field_player = &stored_player[j];
4146 if (field_player != NULL)
4148 int jx = field_player->jx, jy = field_player->jy;
4151 printf("::: found player figure %d\n", field_player->index_nr);
4154 player->present = FALSE;
4155 player->active = FALSE;
4157 field_player->present = TRUE;
4158 field_player->active = TRUE;
4161 player->initial_element = field_player->initial_element;
4162 player->artwork_element = field_player->artwork_element;
4164 player->block_last_field = field_player->block_last_field;
4165 player->block_delay_adjustment = field_player->block_delay_adjustment;
4168 StorePlayer[jx][jy] = field_player->element_nr;
4170 field_player->jx = field_player->last_jx = jx;
4171 field_player->jy = field_player->last_jy = jy;
4173 if (local_player == player)
4174 local_player = field_player;
4176 map_player_action[field_player->index_nr] = i;
4178 field_player->mapped = TRUE;
4181 printf("::: map_player_action[%d] == %d\n",
4182 field_player->index_nr, i);
4187 if (player->connected && player->present)
4188 player->mapped = TRUE;
4193 /* check if any connected player was not found in playfield */
4194 for (i = 0; i < MAX_PLAYERS; i++)
4196 struct PlayerInfo *player = &stored_player[i];
4198 if (player->connected && !player->present)
4200 for (j = 0; j < MAX_PLAYERS; j++)
4202 struct PlayerInfo *field_player = &stored_player[j];
4203 int jx = field_player->jx, jy = field_player->jy;
4205 /* assign first free player found that is present in the playfield */
4206 if (field_player->present && !field_player->connected)
4208 player->present = TRUE;
4209 player->active = TRUE;
4211 field_player->present = FALSE;
4212 field_player->active = FALSE;
4214 player->initial_element = field_player->initial_element;
4215 player->artwork_element = field_player->artwork_element;
4217 player->block_last_field = field_player->block_last_field;
4218 player->block_delay_adjustment = field_player->block_delay_adjustment;
4220 StorePlayer[jx][jy] = player->element_nr;
4222 player->jx = player->last_jx = jx;
4223 player->jy = player->last_jy = jy;
4233 printf("::: local_player->present == %d\n", local_player->present);
4238 /* when playing a tape, eliminate all players who do not participate */
4240 #if USE_NEW_PLAYER_ASSIGNMENTS
4241 for (i = 0; i < MAX_PLAYERS; i++)
4243 if (stored_player[i].active &&
4244 !tape.player_participates[map_player_action[i]])
4246 struct PlayerInfo *player = &stored_player[i];
4247 int jx = player->jx, jy = player->jy;
4249 player->active = FALSE;
4250 StorePlayer[jx][jy] = 0;
4251 Feld[jx][jy] = EL_EMPTY;
4255 for (i = 0; i < MAX_PLAYERS; i++)
4257 if (stored_player[i].active &&
4258 !tape.player_participates[i])
4260 struct PlayerInfo *player = &stored_player[i];
4261 int jx = player->jx, jy = player->jy;
4263 player->active = FALSE;
4264 StorePlayer[jx][jy] = 0;
4265 Feld[jx][jy] = EL_EMPTY;
4270 else if (!options.network && !setup.team_mode) /* && !tape.playing */
4272 /* when in single player mode, eliminate all but the first active player */
4274 for (i = 0; i < MAX_PLAYERS; i++)
4276 if (stored_player[i].active)
4278 for (j = i + 1; j < MAX_PLAYERS; j++)
4280 if (stored_player[j].active)
4282 struct PlayerInfo *player = &stored_player[j];
4283 int jx = player->jx, jy = player->jy;
4285 player->active = FALSE;
4286 player->present = FALSE;
4288 StorePlayer[jx][jy] = 0;
4289 Feld[jx][jy] = EL_EMPTY;
4296 /* when recording the game, store which players take part in the game */
4299 #if USE_NEW_PLAYER_ASSIGNMENTS
4300 for (i = 0; i < MAX_PLAYERS; i++)
4301 if (stored_player[i].connected)
4302 tape.player_participates[i] = TRUE;
4304 for (i = 0; i < MAX_PLAYERS; i++)
4305 if (stored_player[i].active)
4306 tape.player_participates[i] = TRUE;
4312 for (i = 0; i < MAX_PLAYERS; i++)
4314 struct PlayerInfo *player = &stored_player[i];
4316 printf("Player %d: present == %d, connected == %d, active == %d.\n",
4321 if (local_player == player)
4322 printf("Player %d is local player.\n", i+1);
4326 if (BorderElement == EL_EMPTY)
4329 SBX_Right = lev_fieldx - SCR_FIELDX;
4331 SBY_Lower = lev_fieldy - SCR_FIELDY;
4336 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4338 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4341 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4342 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4344 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4345 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4347 /* if local player not found, look for custom element that might create
4348 the player (make some assumptions about the right custom element) */
4349 if (!local_player->present)
4351 int start_x = 0, start_y = 0;
4352 int found_rating = 0;
4353 int found_element = EL_UNDEFINED;
4354 int player_nr = local_player->index_nr;
4356 SCAN_PLAYFIELD(x, y)
4358 int element = Feld[x][y];
4363 if (level.use_start_element[player_nr] &&
4364 level.start_element[player_nr] == element &&
4371 found_element = element;
4374 if (!IS_CUSTOM_ELEMENT(element))
4377 if (CAN_CHANGE(element))
4379 for (i = 0; i < element_info[element].num_change_pages; i++)
4381 /* check for player created from custom element as single target */
4382 content = element_info[element].change_page[i].target_element;
4383 is_player = ELEM_IS_PLAYER(content);
4385 if (is_player && (found_rating < 3 ||
4386 (found_rating == 3 && element < found_element)))
4392 found_element = element;
4397 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4399 /* check for player created from custom element as explosion content */
4400 content = element_info[element].content.e[xx][yy];
4401 is_player = ELEM_IS_PLAYER(content);
4403 if (is_player && (found_rating < 2 ||
4404 (found_rating == 2 && element < found_element)))
4406 start_x = x + xx - 1;
4407 start_y = y + yy - 1;
4410 found_element = element;
4413 if (!CAN_CHANGE(element))
4416 for (i = 0; i < element_info[element].num_change_pages; i++)
4418 /* check for player created from custom element as extended target */
4420 element_info[element].change_page[i].target_content.e[xx][yy];
4422 is_player = ELEM_IS_PLAYER(content);
4424 if (is_player && (found_rating < 1 ||
4425 (found_rating == 1 && element < found_element)))
4427 start_x = x + xx - 1;
4428 start_y = y + yy - 1;
4431 found_element = element;
4437 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
4438 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4441 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4442 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4447 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
4448 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4449 local_player->jx - MIDPOSX);
4451 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4452 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4453 local_player->jy - MIDPOSY);
4457 /* do not use PLAYING mask for fading out from main screen */
4458 game_status = GAME_MODE_MAIN;
4463 if (!game.restart_level)
4464 CloseDoor(DOOR_CLOSE_1);
4467 if (level_editor_test_game)
4468 FadeSkipNextFadeIn();
4470 FadeSetEnterScreen();
4472 if (level_editor_test_game)
4473 fading = fading_none;
4475 fading = menu.destination;
4479 FadeOut(REDRAW_FIELD);
4482 FadeOut(REDRAW_FIELD);
4486 game_status = GAME_MODE_PLAYING;
4489 /* !!! FIX THIS (START) !!! */
4490 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4492 InitGameEngine_EM();
4494 /* blit playfield from scroll buffer to normal back buffer for fading in */
4495 BlitScreenToBitmap_EM(backbuffer);
4497 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4499 InitGameEngine_SP();
4501 /* blit playfield from scroll buffer to normal back buffer for fading in */
4502 BlitScreenToBitmap_SP(backbuffer);
4509 /* after drawing the level, correct some elements */
4510 if (game.timegate_time_left == 0)
4511 CloseAllOpenTimegates();
4513 /* blit playfield from scroll buffer to normal back buffer for fading in */
4514 if (setup.soft_scrolling)
4515 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4517 redraw_mask |= REDRAW_FROM_BACKBUFFER;
4519 /* !!! FIX THIS (END) !!! */
4522 FadeIn(REDRAW_FIELD);
4525 FadeIn(REDRAW_FIELD);
4530 if (!game.restart_level)
4532 /* copy default game door content to main double buffer */
4533 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4534 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4537 SetPanelBackground();
4538 SetDrawBackgroundMask(REDRAW_DOOR_1);
4541 UpdateAndDisplayGameControlValues();
4543 UpdateGameDoorValues();
4544 DrawGameDoorValues();
4547 if (!game.restart_level)
4551 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4552 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4553 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4557 /* copy actual game door content to door double buffer for OpenDoor() */
4558 BlitBitmap(drawto, bitmap_db_door,
4559 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4561 OpenDoor(DOOR_OPEN_ALL);
4563 PlaySound(SND_GAME_STARTING);
4565 if (setup.sound_music)
4568 KeyboardAutoRepeatOffUnlessAutoplay();
4572 for (i = 0; i < MAX_PLAYERS; i++)
4573 printf("Player %d %sactive.\n",
4574 i + 1, (stored_player[i].active ? "" : "not "));
4585 game.restart_level = FALSE;
4588 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4590 /* this is used for non-R'n'D game engines to update certain engine values */
4592 /* needed to determine if sounds are played within the visible screen area */
4593 scroll_x = actual_scroll_x;
4594 scroll_y = actual_scroll_y;
4597 void InitMovDir(int x, int y)
4599 int i, element = Feld[x][y];
4600 static int xy[4][2] =
4607 static int direction[3][4] =
4609 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4610 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4611 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4620 Feld[x][y] = EL_BUG;
4621 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4624 case EL_SPACESHIP_RIGHT:
4625 case EL_SPACESHIP_UP:
4626 case EL_SPACESHIP_LEFT:
4627 case EL_SPACESHIP_DOWN:
4628 Feld[x][y] = EL_SPACESHIP;
4629 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4632 case EL_BD_BUTTERFLY_RIGHT:
4633 case EL_BD_BUTTERFLY_UP:
4634 case EL_BD_BUTTERFLY_LEFT:
4635 case EL_BD_BUTTERFLY_DOWN:
4636 Feld[x][y] = EL_BD_BUTTERFLY;
4637 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4640 case EL_BD_FIREFLY_RIGHT:
4641 case EL_BD_FIREFLY_UP:
4642 case EL_BD_FIREFLY_LEFT:
4643 case EL_BD_FIREFLY_DOWN:
4644 Feld[x][y] = EL_BD_FIREFLY;
4645 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4648 case EL_PACMAN_RIGHT:
4650 case EL_PACMAN_LEFT:
4651 case EL_PACMAN_DOWN:
4652 Feld[x][y] = EL_PACMAN;
4653 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4656 case EL_YAMYAM_LEFT:
4657 case EL_YAMYAM_RIGHT:
4659 case EL_YAMYAM_DOWN:
4660 Feld[x][y] = EL_YAMYAM;
4661 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4664 case EL_SP_SNIKSNAK:
4665 MovDir[x][y] = MV_UP;
4668 case EL_SP_ELECTRON:
4669 MovDir[x][y] = MV_LEFT;
4676 Feld[x][y] = EL_MOLE;
4677 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4681 if (IS_CUSTOM_ELEMENT(element))
4683 struct ElementInfo *ei = &element_info[element];
4684 int move_direction_initial = ei->move_direction_initial;
4685 int move_pattern = ei->move_pattern;
4687 if (move_direction_initial == MV_START_PREVIOUS)
4689 if (MovDir[x][y] != MV_NONE)
4692 move_direction_initial = MV_START_AUTOMATIC;
4695 if (move_direction_initial == MV_START_RANDOM)
4696 MovDir[x][y] = 1 << RND(4);
4697 else if (move_direction_initial & MV_ANY_DIRECTION)
4698 MovDir[x][y] = move_direction_initial;
4699 else if (move_pattern == MV_ALL_DIRECTIONS ||
4700 move_pattern == MV_TURNING_LEFT ||
4701 move_pattern == MV_TURNING_RIGHT ||
4702 move_pattern == MV_TURNING_LEFT_RIGHT ||
4703 move_pattern == MV_TURNING_RIGHT_LEFT ||
4704 move_pattern == MV_TURNING_RANDOM)
4705 MovDir[x][y] = 1 << RND(4);
4706 else if (move_pattern == MV_HORIZONTAL)
4707 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4708 else if (move_pattern == MV_VERTICAL)
4709 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4710 else if (move_pattern & MV_ANY_DIRECTION)
4711 MovDir[x][y] = element_info[element].move_pattern;
4712 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4713 move_pattern == MV_ALONG_RIGHT_SIDE)
4715 /* use random direction as default start direction */
4716 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4717 MovDir[x][y] = 1 << RND(4);
4719 for (i = 0; i < NUM_DIRECTIONS; i++)
4721 int x1 = x + xy[i][0];
4722 int y1 = y + xy[i][1];
4724 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4726 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4727 MovDir[x][y] = direction[0][i];
4729 MovDir[x][y] = direction[1][i];
4738 MovDir[x][y] = 1 << RND(4);
4740 if (element != EL_BUG &&
4741 element != EL_SPACESHIP &&
4742 element != EL_BD_BUTTERFLY &&
4743 element != EL_BD_FIREFLY)
4746 for (i = 0; i < NUM_DIRECTIONS; i++)
4748 int x1 = x + xy[i][0];
4749 int y1 = y + xy[i][1];
4751 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4753 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4755 MovDir[x][y] = direction[0][i];
4758 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4759 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4761 MovDir[x][y] = direction[1][i];
4770 GfxDir[x][y] = MovDir[x][y];
4773 void InitAmoebaNr(int x, int y)
4776 int group_nr = AmoebeNachbarNr(x, y);
4780 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4782 if (AmoebaCnt[i] == 0)
4790 AmoebaNr[x][y] = group_nr;
4791 AmoebaCnt[group_nr]++;
4792 AmoebaCnt2[group_nr]++;
4795 static void PlayerWins(struct PlayerInfo *player)
4797 player->LevelSolved = TRUE;
4798 player->GameOver = TRUE;
4800 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4801 level.native_em_level->lev->score : player->score);
4803 player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4804 player->LevelSolved_CountingScore = player->score_final;
4809 static int time, time_final;
4810 static int score, score_final;
4811 static int game_over_delay_1 = 0;
4812 static int game_over_delay_2 = 0;
4813 int game_over_delay_value_1 = 50;
4814 int game_over_delay_value_2 = 50;
4816 if (!local_player->LevelSolved_GameWon)
4820 /* do not start end game actions before the player stops moving (to exit) */
4821 if (local_player->MovPos)
4824 local_player->LevelSolved_GameWon = TRUE;
4825 local_player->LevelSolved_SaveTape = tape.recording;
4826 local_player->LevelSolved_SaveScore = !tape.playing;
4828 if (tape.auto_play) /* tape might already be stopped here */
4829 tape.auto_play_level_solved = TRUE;
4835 game_over_delay_1 = game_over_delay_value_1;
4836 game_over_delay_2 = game_over_delay_value_2;
4838 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4839 score = score_final = local_player->score_final;
4844 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4846 else if (level.time == 0 && TimePlayed < 999)
4849 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4852 local_player->score_final = score_final;
4854 if (level_editor_test_game)
4857 score = score_final;
4860 local_player->LevelSolved_CountingTime = time;
4861 local_player->LevelSolved_CountingScore = score;
4863 game_panel_controls[GAME_PANEL_TIME].value = time;
4864 game_panel_controls[GAME_PANEL_SCORE].value = score;
4866 DisplayGameControlValues();
4868 DrawGameValue_Time(time);
4869 DrawGameValue_Score(score);
4873 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4875 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4877 /* close exit door after last player */
4878 if ((AllPlayersGone &&
4879 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4880 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4881 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4882 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4883 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4885 int element = Feld[ExitX][ExitY];
4888 if (element == EL_EM_EXIT_OPEN ||
4889 element == EL_EM_STEEL_EXIT_OPEN)
4896 Feld[ExitX][ExitY] =
4897 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4898 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4899 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4900 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4901 EL_EM_STEEL_EXIT_CLOSING);
4903 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4907 /* player disappears */
4908 DrawLevelField(ExitX, ExitY);
4911 for (i = 0; i < MAX_PLAYERS; i++)
4913 struct PlayerInfo *player = &stored_player[i];
4915 if (player->present)
4917 RemovePlayer(player);
4919 /* player disappears */
4920 DrawLevelField(player->jx, player->jy);
4925 PlaySound(SND_GAME_WINNING);
4928 if (game_over_delay_1 > 0)
4930 game_over_delay_1--;
4935 if (time != time_final)
4937 int time_to_go = ABS(time_final - time);
4938 int time_count_dir = (time < time_final ? +1 : -1);
4939 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4941 time += time_count_steps * time_count_dir;
4942 score += time_count_steps * level.score[SC_TIME_BONUS];
4945 local_player->LevelSolved_CountingTime = time;
4946 local_player->LevelSolved_CountingScore = score;
4948 game_panel_controls[GAME_PANEL_TIME].value = time;
4949 game_panel_controls[GAME_PANEL_SCORE].value = score;
4951 DisplayGameControlValues();
4953 DrawGameValue_Time(time);
4954 DrawGameValue_Score(score);
4957 if (time == time_final)
4958 StopSound(SND_GAME_LEVELTIME_BONUS);
4959 else if (setup.sound_loops)
4960 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4962 PlaySound(SND_GAME_LEVELTIME_BONUS);
4967 local_player->LevelSolved_PanelOff = TRUE;
4969 if (game_over_delay_2 > 0)
4971 game_over_delay_2--;
4984 boolean raise_level = FALSE;
4986 local_player->LevelSolved_GameEnd = TRUE;
4988 CloseDoor(DOOR_CLOSE_1);
4990 if (local_player->LevelSolved_SaveTape)
4997 SaveTapeChecked(tape.level_nr); /* ask to save tape */
4999 SaveTape(tape.level_nr); /* ask to save tape */
5003 if (level_editor_test_game)
5005 game_status = GAME_MODE_MAIN;
5008 DrawAndFadeInMainMenu(REDRAW_FIELD);
5016 if (!local_player->LevelSolved_SaveScore)
5019 FadeOut(REDRAW_FIELD);
5022 game_status = GAME_MODE_MAIN;
5024 DrawAndFadeInMainMenu(REDRAW_FIELD);
5029 if (level_nr == leveldir_current->handicap_level)
5031 leveldir_current->handicap_level++;
5032 SaveLevelSetup_SeriesInfo();
5035 if (level_nr < leveldir_current->last_level)
5036 raise_level = TRUE; /* advance to next level */
5038 if ((hi_pos = NewHiScore()) >= 0)
5040 game_status = GAME_MODE_SCORES;
5042 DrawHallOfFame(hi_pos);
5053 FadeOut(REDRAW_FIELD);
5056 game_status = GAME_MODE_MAIN;
5064 DrawAndFadeInMainMenu(REDRAW_FIELD);
5073 LoadScore(level_nr);
5075 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5076 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
5079 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
5081 if (local_player->score_final > highscore[k].Score)
5083 /* player has made it to the hall of fame */
5085 if (k < MAX_SCORE_ENTRIES - 1)
5087 int m = MAX_SCORE_ENTRIES - 1;
5090 for (l = k; l < MAX_SCORE_ENTRIES; l++)
5091 if (strEqual(setup.player_name, highscore[l].Name))
5093 if (m == k) /* player's new highscore overwrites his old one */
5097 for (l = m; l > k; l--)
5099 strcpy(highscore[l].Name, highscore[l - 1].Name);
5100 highscore[l].Score = highscore[l - 1].Score;
5107 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5108 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5109 highscore[k].Score = local_player->score_final;
5115 else if (!strncmp(setup.player_name, highscore[k].Name,
5116 MAX_PLAYER_NAME_LEN))
5117 break; /* player already there with a higher score */
5123 SaveScore(level_nr);
5128 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
5130 int element = Feld[x][y];
5131 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5132 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5133 int horiz_move = (dx != 0);
5134 int sign = (horiz_move ? dx : dy);
5135 int step = sign * element_info[element].move_stepsize;
5137 /* special values for move stepsize for spring and things on conveyor belt */
5140 if (CAN_FALL(element) &&
5141 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5142 step = sign * MOVE_STEPSIZE_NORMAL / 2;
5143 else if (element == EL_SPRING)
5144 step = sign * MOVE_STEPSIZE_NORMAL * 2;
5150 inline static int getElementMoveStepsize(int x, int y)
5152 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5155 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5157 if (player->GfxAction != action || player->GfxDir != dir)
5160 printf("Player frame reset! (%d => %d, %d => %d)\n",
5161 player->GfxAction, action, player->GfxDir, dir);
5164 player->GfxAction = action;
5165 player->GfxDir = dir;
5167 player->StepFrame = 0;
5171 #if USE_GFX_RESET_GFX_ANIMATION
5172 static void ResetGfxFrame(int x, int y, boolean redraw)
5174 int element = Feld[x][y];
5175 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5176 int last_gfx_frame = GfxFrame[x][y];
5178 if (graphic_info[graphic].anim_global_sync)
5179 GfxFrame[x][y] = FrameCounter;
5180 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5181 GfxFrame[x][y] = CustomValue[x][y];
5182 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5183 GfxFrame[x][y] = element_info[element].collect_score;
5184 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5185 GfxFrame[x][y] = ChangeDelay[x][y];
5187 if (redraw && GfxFrame[x][y] != last_gfx_frame)
5188 DrawLevelGraphicAnimation(x, y, graphic);
5192 static void ResetGfxAnimation(int x, int y)
5194 GfxAction[x][y] = ACTION_DEFAULT;
5195 GfxDir[x][y] = MovDir[x][y];
5198 #if USE_GFX_RESET_GFX_ANIMATION
5199 ResetGfxFrame(x, y, FALSE);
5203 static void ResetRandomAnimationValue(int x, int y)
5205 GfxRandom[x][y] = INIT_GFX_RANDOM();
5208 void InitMovingField(int x, int y, int direction)
5210 int element = Feld[x][y];
5211 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5212 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5215 boolean is_moving_before, is_moving_after;
5217 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5220 /* check if element was/is moving or being moved before/after mode change */
5223 is_moving_before = (WasJustMoving[x][y] != 0);
5225 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5226 is_moving_before = WasJustMoving[x][y];
5229 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5231 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
5233 /* reset animation only for moving elements which change direction of moving
5234 or which just started or stopped moving
5235 (else CEs with property "can move" / "not moving" are reset each frame) */
5236 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5238 if (is_moving_before != is_moving_after ||
5239 direction != MovDir[x][y])
5240 ResetGfxAnimation(x, y);
5242 if ((is_moving_before || is_moving_after) && !continues_moving)
5243 ResetGfxAnimation(x, y);
5246 if (!continues_moving)
5247 ResetGfxAnimation(x, y);
5250 MovDir[x][y] = direction;
5251 GfxDir[x][y] = direction;
5253 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5254 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5255 direction == MV_DOWN && CAN_FALL(element) ?
5256 ACTION_FALLING : ACTION_MOVING);
5258 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5259 ACTION_FALLING : ACTION_MOVING);
5262 /* this is needed for CEs with property "can move" / "not moving" */
5264 if (is_moving_after)
5266 if (Feld[newx][newy] == EL_EMPTY)
5267 Feld[newx][newy] = EL_BLOCKED;
5269 MovDir[newx][newy] = MovDir[x][y];
5271 #if USE_NEW_CUSTOM_VALUE
5272 CustomValue[newx][newy] = CustomValue[x][y];
5275 GfxFrame[newx][newy] = GfxFrame[x][y];
5276 GfxRandom[newx][newy] = GfxRandom[x][y];
5277 GfxAction[newx][newy] = GfxAction[x][y];
5278 GfxDir[newx][newy] = GfxDir[x][y];
5282 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5284 int direction = MovDir[x][y];
5285 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5286 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
5292 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5294 int oldx = x, oldy = y;
5295 int direction = MovDir[x][y];
5297 if (direction == MV_LEFT)
5299 else if (direction == MV_RIGHT)
5301 else if (direction == MV_UP)
5303 else if (direction == MV_DOWN)
5306 *comes_from_x = oldx;
5307 *comes_from_y = oldy;
5310 int MovingOrBlocked2Element(int x, int y)
5312 int element = Feld[x][y];
5314 if (element == EL_BLOCKED)
5318 Blocked2Moving(x, y, &oldx, &oldy);
5319 return Feld[oldx][oldy];
5325 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5327 /* like MovingOrBlocked2Element(), but if element is moving
5328 and (x,y) is the field the moving element is just leaving,
5329 return EL_BLOCKED instead of the element value */
5330 int element = Feld[x][y];
5332 if (IS_MOVING(x, y))
5334 if (element == EL_BLOCKED)
5338 Blocked2Moving(x, y, &oldx, &oldy);
5339 return Feld[oldx][oldy];
5348 static void RemoveField(int x, int y)
5350 Feld[x][y] = EL_EMPTY;
5356 #if USE_NEW_CUSTOM_VALUE
5357 CustomValue[x][y] = 0;
5361 ChangeDelay[x][y] = 0;
5362 ChangePage[x][y] = -1;
5363 Pushed[x][y] = FALSE;
5366 ExplodeField[x][y] = EX_TYPE_NONE;
5369 GfxElement[x][y] = EL_UNDEFINED;
5370 GfxAction[x][y] = ACTION_DEFAULT;
5371 GfxDir[x][y] = MV_NONE;
5373 /* !!! this would prevent the removed tile from being redrawn !!! */
5374 GfxRedraw[x][y] = GFX_REDRAW_NONE;
5378 void RemoveMovingField(int x, int y)
5380 int oldx = x, oldy = y, newx = x, newy = y;
5381 int element = Feld[x][y];
5382 int next_element = EL_UNDEFINED;
5384 if (element != EL_BLOCKED && !IS_MOVING(x, y))
5387 if (IS_MOVING(x, y))
5389 Moving2Blocked(x, y, &newx, &newy);
5391 if (Feld[newx][newy] != EL_BLOCKED)
5393 /* element is moving, but target field is not free (blocked), but
5394 already occupied by something different (example: acid pool);
5395 in this case, only remove the moving field, but not the target */
5397 RemoveField(oldx, oldy);
5399 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5401 TEST_DrawLevelField(oldx, oldy);
5406 else if (element == EL_BLOCKED)
5408 Blocked2Moving(x, y, &oldx, &oldy);
5409 if (!IS_MOVING(oldx, oldy))
5413 if (element == EL_BLOCKED &&
5414 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5415 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5416 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5417 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5418 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5419 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5420 next_element = get_next_element(Feld[oldx][oldy]);
5422 RemoveField(oldx, oldy);
5423 RemoveField(newx, newy);
5425 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5427 if (next_element != EL_UNDEFINED)
5428 Feld[oldx][oldy] = next_element;
5430 TEST_DrawLevelField(oldx, oldy);
5431 TEST_DrawLevelField(newx, newy);
5434 void DrawDynamite(int x, int y)
5436 int sx = SCREENX(x), sy = SCREENY(y);
5437 int graphic = el2img(Feld[x][y]);
5440 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5443 if (IS_WALKABLE_INSIDE(Back[x][y]))
5447 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5448 else if (Store[x][y])
5449 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5451 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5453 if (Back[x][y] || Store[x][y])
5454 DrawGraphicThruMask(sx, sy, graphic, frame);
5456 DrawGraphic(sx, sy, graphic, frame);
5459 void CheckDynamite(int x, int y)
5461 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5465 if (MovDelay[x][y] != 0)
5468 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5474 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5479 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5481 boolean num_checked_players = 0;
5484 for (i = 0; i < MAX_PLAYERS; i++)
5486 if (stored_player[i].active)
5488 int sx = stored_player[i].jx;
5489 int sy = stored_player[i].jy;
5491 if (num_checked_players == 0)
5498 *sx1 = MIN(*sx1, sx);
5499 *sy1 = MIN(*sy1, sy);
5500 *sx2 = MAX(*sx2, sx);
5501 *sy2 = MAX(*sy2, sy);
5504 num_checked_players++;
5509 static boolean checkIfAllPlayersFitToScreen_RND()
5511 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5513 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5515 return (sx2 - sx1 < SCR_FIELDX &&
5516 sy2 - sy1 < SCR_FIELDY);
5519 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5521 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5523 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5525 *sx = (sx1 + sx2) / 2;
5526 *sy = (sy1 + sy2) / 2;
5529 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5530 boolean center_screen, boolean quick_relocation)
5532 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5533 boolean no_delay = (tape.warp_forward);
5534 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5535 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5537 if (quick_relocation)
5539 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5541 if (!level.shifted_relocation || center_screen)
5543 /* quick relocation (without scrolling), with centering of screen */
5545 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5546 x > SBX_Right + MIDPOSX ? SBX_Right :
5549 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5550 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5555 /* quick relocation (without scrolling), but do not center screen */
5557 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5558 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5561 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5562 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5565 int offset_x = x + (scroll_x - center_scroll_x);
5566 int offset_y = y + (scroll_y - center_scroll_y);
5568 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5569 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5570 offset_x - MIDPOSX);
5572 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5573 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5574 offset_y - MIDPOSY);
5580 if (!level.shifted_relocation || center_screen)
5582 /* quick relocation (without scrolling), with centering of screen */
5584 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5585 x > SBX_Right + MIDPOSX ? SBX_Right :
5588 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5589 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5594 /* quick relocation (without scrolling), but do not center screen */
5596 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5597 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5600 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5601 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5604 int offset_x = x + (scroll_x - center_scroll_x);
5605 int offset_y = y + (scroll_y - center_scroll_y);
5607 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5608 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5609 offset_x - MIDPOSX);
5611 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5612 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5613 offset_y - MIDPOSY);
5616 /* quick relocation (without scrolling), inside visible screen area */
5618 int offset = game.scroll_delay_value;
5620 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
5621 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5622 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5624 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
5625 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5626 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5628 /* don't scroll over playfield boundaries */
5629 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5630 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5632 /* don't scroll over playfield boundaries */
5633 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5634 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5638 RedrawPlayfield(TRUE, 0,0,0,0);
5643 int scroll_xx, scroll_yy;
5645 if (!level.shifted_relocation || center_screen)
5647 /* visible relocation (with scrolling), with centering of screen */
5649 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5650 x > SBX_Right + MIDPOSX ? SBX_Right :
5653 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5654 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5659 /* visible relocation (with scrolling), but do not center screen */
5661 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5662 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5665 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5666 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5669 int offset_x = x + (scroll_x - center_scroll_x);
5670 int offset_y = y + (scroll_y - center_scroll_y);
5672 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5673 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5674 offset_x - MIDPOSX);
5676 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5677 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5678 offset_y - MIDPOSY);
5683 /* visible relocation (with scrolling), with centering of screen */
5685 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5686 x > SBX_Right + MIDPOSX ? SBX_Right :
5689 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5690 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5694 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5696 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5699 int fx = FX, fy = FY;
5701 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5702 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5704 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5710 fx += dx * TILEX / 2;
5711 fy += dy * TILEY / 2;
5713 ScrollLevel(dx, dy);
5716 /* scroll in two steps of half tile size to make things smoother */
5717 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5719 Delay(wait_delay_value);
5721 /* scroll second step to align at full tile size */
5723 Delay(wait_delay_value);
5728 Delay(wait_delay_value);
5732 void RelocatePlayer(int jx, int jy, int el_player_raw)
5734 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5735 int player_nr = GET_PLAYER_NR(el_player);
5736 struct PlayerInfo *player = &stored_player[player_nr];
5737 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5738 boolean no_delay = (tape.warp_forward);
5739 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5740 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5741 int old_jx = player->jx;
5742 int old_jy = player->jy;
5743 int old_element = Feld[old_jx][old_jy];
5744 int element = Feld[jx][jy];
5745 boolean player_relocated = (old_jx != jx || old_jy != jy);
5747 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5748 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5749 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5750 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5751 int leave_side_horiz = move_dir_horiz;
5752 int leave_side_vert = move_dir_vert;
5753 int enter_side = enter_side_horiz | enter_side_vert;
5754 int leave_side = leave_side_horiz | leave_side_vert;
5756 if (player->GameOver) /* do not reanimate dead player */
5759 if (!player_relocated) /* no need to relocate the player */
5762 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5764 RemoveField(jx, jy); /* temporarily remove newly placed player */
5765 DrawLevelField(jx, jy);
5768 if (player->present)
5770 while (player->MovPos)
5772 ScrollPlayer(player, SCROLL_GO_ON);
5773 ScrollScreen(NULL, SCROLL_GO_ON);
5775 AdvanceFrameAndPlayerCounters(player->index_nr);
5780 Delay(wait_delay_value);
5783 DrawPlayer(player); /* needed here only to cleanup last field */
5784 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5786 player->is_moving = FALSE;
5789 if (IS_CUSTOM_ELEMENT(old_element))
5790 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5792 player->index_bit, leave_side);
5794 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5796 player->index_bit, leave_side);
5798 Feld[jx][jy] = el_player;
5799 InitPlayerField(jx, jy, el_player, TRUE);
5801 /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5802 possible that the relocation target field did not contain a player element,
5803 but a walkable element, to which the new player was relocated -- in this
5804 case, restore that (already initialized!) element on the player field */
5805 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5807 Feld[jx][jy] = element; /* restore previously existing element */
5809 /* !!! do not initialize already initialized element a second time !!! */
5810 /* (this causes at least problems with "element creation" CE trigger for
5811 already existing elements, and existing Sokoban fields counted twice) */
5812 InitField(jx, jy, FALSE);
5816 /* only visually relocate centered player */
5817 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5818 FALSE, level.instant_relocation);
5820 TestIfPlayerTouchesBadThing(jx, jy);
5821 TestIfPlayerTouchesCustomElement(jx, jy);
5823 if (IS_CUSTOM_ELEMENT(element))
5824 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5825 player->index_bit, enter_side);
5827 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5828 player->index_bit, enter_side);
5831 if (player->is_switching)
5833 /* ensure that relocation while still switching an element does not cause
5834 a new element to be treated as also switched directly after relocation
5835 (this is important for teleporter switches that teleport the player to
5836 a place where another teleporter switch is in the same direction, which
5837 would then incorrectly be treated as immediately switched before the
5838 direction key that caused the switch was released) */
5840 player->switch_x += jx - old_jx;
5841 player->switch_y += jy - old_jy;
5846 void Explode(int ex, int ey, int phase, int mode)
5852 /* !!! eliminate this variable !!! */
5853 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5855 if (game.explosions_delayed)
5857 ExplodeField[ex][ey] = mode;
5861 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5863 int center_element = Feld[ex][ey];
5864 int artwork_element, explosion_element; /* set these values later */
5867 /* --- This is only really needed (and now handled) in "Impact()". --- */
5868 /* do not explode moving elements that left the explode field in time */
5869 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5870 center_element == EL_EMPTY &&
5871 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5876 /* !!! at this place, the center element may be EL_BLOCKED !!! */
5877 if (mode == EX_TYPE_NORMAL ||
5878 mode == EX_TYPE_CENTER ||
5879 mode == EX_TYPE_CROSS)
5880 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5883 /* remove things displayed in background while burning dynamite */
5884 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5887 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5889 /* put moving element to center field (and let it explode there) */
5890 center_element = MovingOrBlocked2Element(ex, ey);
5891 RemoveMovingField(ex, ey);
5892 Feld[ex][ey] = center_element;
5895 /* now "center_element" is finally determined -- set related values now */
5896 artwork_element = center_element; /* for custom player artwork */
5897 explosion_element = center_element; /* for custom player artwork */
5899 if (IS_PLAYER(ex, ey))
5901 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5903 artwork_element = stored_player[player_nr].artwork_element;
5905 if (level.use_explosion_element[player_nr])
5907 explosion_element = level.explosion_element[player_nr];
5908 artwork_element = explosion_element;
5913 if (mode == EX_TYPE_NORMAL ||
5914 mode == EX_TYPE_CENTER ||
5915 mode == EX_TYPE_CROSS)
5916 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5919 last_phase = element_info[explosion_element].explosion_delay + 1;
5921 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5923 int xx = x - ex + 1;
5924 int yy = y - ey + 1;
5927 if (!IN_LEV_FIELD(x, y) ||
5928 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5929 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5932 element = Feld[x][y];
5934 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5936 element = MovingOrBlocked2Element(x, y);
5938 if (!IS_EXPLOSION_PROOF(element))
5939 RemoveMovingField(x, y);
5942 /* indestructible elements can only explode in center (but not flames) */
5943 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5944 mode == EX_TYPE_BORDER)) ||
5945 element == EL_FLAMES)
5948 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5949 behaviour, for example when touching a yamyam that explodes to rocks
5950 with active deadly shield, a rock is created under the player !!! */
5951 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5953 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5954 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5955 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5957 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5960 if (IS_ACTIVE_BOMB(element))
5962 /* re-activate things under the bomb like gate or penguin */
5963 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5970 /* save walkable background elements while explosion on same tile */
5971 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5972 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5973 Back[x][y] = element;
5975 /* ignite explodable elements reached by other explosion */
5976 if (element == EL_EXPLOSION)
5977 element = Store2[x][y];
5979 if (AmoebaNr[x][y] &&
5980 (element == EL_AMOEBA_FULL ||
5981 element == EL_BD_AMOEBA ||
5982 element == EL_AMOEBA_GROWING))
5984 AmoebaCnt[AmoebaNr[x][y]]--;
5985 AmoebaCnt2[AmoebaNr[x][y]]--;
5990 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5992 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5994 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5996 if (PLAYERINFO(ex, ey)->use_murphy)
5997 Store[x][y] = EL_EMPTY;
6000 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
6001 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
6002 else if (ELEM_IS_PLAYER(center_element))
6003 Store[x][y] = EL_EMPTY;
6004 else if (center_element == EL_YAMYAM)
6005 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
6006 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
6007 Store[x][y] = element_info[center_element].content.e[xx][yy];
6009 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6010 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6011 otherwise) -- FIX THIS !!! */
6012 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6013 Store[x][y] = element_info[element].content.e[1][1];
6015 else if (!CAN_EXPLODE(element))
6016 Store[x][y] = element_info[element].content.e[1][1];
6019 Store[x][y] = EL_EMPTY;
6021 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6022 center_element == EL_AMOEBA_TO_DIAMOND)
6023 Store2[x][y] = element;
6025 Feld[x][y] = EL_EXPLOSION;
6026 GfxElement[x][y] = artwork_element;
6028 ExplodePhase[x][y] = 1;
6029 ExplodeDelay[x][y] = last_phase;
6034 if (center_element == EL_YAMYAM)
6035 game.yamyam_content_nr =
6036 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6048 GfxFrame[x][y] = 0; /* restart explosion animation */
6050 last_phase = ExplodeDelay[x][y];
6052 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6056 /* activate this even in non-DEBUG version until cause for crash in
6057 getGraphicAnimationFrame() (see below) is found and eliminated */
6063 /* this can happen if the player leaves an explosion just in time */
6064 if (GfxElement[x][y] == EL_UNDEFINED)
6065 GfxElement[x][y] = EL_EMPTY;
6067 if (GfxElement[x][y] == EL_UNDEFINED)
6070 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
6071 printf("Explode(): This should never happen!\n");
6074 GfxElement[x][y] = EL_EMPTY;
6080 border_element = Store2[x][y];
6081 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6082 border_element = StorePlayer[x][y];
6084 if (phase == element_info[border_element].ignition_delay ||
6085 phase == last_phase)
6087 boolean border_explosion = FALSE;
6089 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6090 !PLAYER_EXPLOSION_PROTECTED(x, y))
6092 KillPlayerUnlessExplosionProtected(x, y);
6093 border_explosion = TRUE;
6095 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6097 Feld[x][y] = Store2[x][y];
6100 border_explosion = TRUE;
6102 else if (border_element == EL_AMOEBA_TO_DIAMOND)
6104 AmoebeUmwandeln(x, y);
6106 border_explosion = TRUE;
6109 /* if an element just explodes due to another explosion (chain-reaction),
6110 do not immediately end the new explosion when it was the last frame of
6111 the explosion (as it would be done in the following "if"-statement!) */
6112 if (border_explosion && phase == last_phase)
6116 if (phase == last_phase)
6120 element = Feld[x][y] = Store[x][y];
6121 Store[x][y] = Store2[x][y] = 0;
6122 GfxElement[x][y] = EL_UNDEFINED;
6124 /* player can escape from explosions and might therefore be still alive */
6125 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6126 element <= EL_PLAYER_IS_EXPLODING_4)
6128 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6129 int explosion_element = EL_PLAYER_1 + player_nr;
6130 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6131 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6133 if (level.use_explosion_element[player_nr])
6134 explosion_element = level.explosion_element[player_nr];
6136 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6137 element_info[explosion_element].content.e[xx][yy]);
6140 /* restore probably existing indestructible background element */
6141 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6142 element = Feld[x][y] = Back[x][y];
6145 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6146 GfxDir[x][y] = MV_NONE;
6147 ChangeDelay[x][y] = 0;
6148 ChangePage[x][y] = -1;
6150 #if USE_NEW_CUSTOM_VALUE
6151 CustomValue[x][y] = 0;
6154 InitField_WithBug2(x, y, FALSE);
6156 TEST_DrawLevelField(x, y);
6158 TestIfElementTouchesCustomElement(x, y);
6160 if (GFX_CRUMBLED(element))
6161 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6163 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6164 StorePlayer[x][y] = 0;
6166 if (ELEM_IS_PLAYER(element))
6167 RelocatePlayer(x, y, element);
6169 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6171 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6172 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6175 TEST_DrawLevelFieldCrumbled(x, y);
6177 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6179 DrawLevelElement(x, y, Back[x][y]);
6180 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6182 else if (IS_WALKABLE_UNDER(Back[x][y]))
6184 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6185 DrawLevelElementThruMask(x, y, Back[x][y]);
6187 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6188 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6192 void DynaExplode(int ex, int ey)
6195 int dynabomb_element = Feld[ex][ey];
6196 int dynabomb_size = 1;
6197 boolean dynabomb_xl = FALSE;
6198 struct PlayerInfo *player;
6199 static int xy[4][2] =
6207 if (IS_ACTIVE_BOMB(dynabomb_element))
6209 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6210 dynabomb_size = player->dynabomb_size;
6211 dynabomb_xl = player->dynabomb_xl;
6212 player->dynabombs_left++;
6215 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6217 for (i = 0; i < NUM_DIRECTIONS; i++)
6219 for (j = 1; j <= dynabomb_size; j++)
6221 int x = ex + j * xy[i][0];
6222 int y = ey + j * xy[i][1];
6225 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6228 element = Feld[x][y];
6230 /* do not restart explosions of fields with active bombs */
6231 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6234 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6236 if (element != EL_EMPTY && element != EL_EXPLOSION &&
6237 !IS_DIGGABLE(element) && !dynabomb_xl)
6243 void Bang(int x, int y)
6245 int element = MovingOrBlocked2Element(x, y);
6246 int explosion_type = EX_TYPE_NORMAL;
6248 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6250 struct PlayerInfo *player = PLAYERINFO(x, y);
6252 #if USE_FIX_CE_ACTION_WITH_PLAYER
6253 element = Feld[x][y] = player->initial_element;
6255 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6256 player->element_nr);
6259 if (level.use_explosion_element[player->index_nr])
6261 int explosion_element = level.explosion_element[player->index_nr];
6263 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6264 explosion_type = EX_TYPE_CROSS;
6265 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6266 explosion_type = EX_TYPE_CENTER;
6274 case EL_BD_BUTTERFLY:
6277 case EL_DARK_YAMYAM:
6281 RaiseScoreElement(element);
6284 case EL_DYNABOMB_PLAYER_1_ACTIVE:
6285 case EL_DYNABOMB_PLAYER_2_ACTIVE:
6286 case EL_DYNABOMB_PLAYER_3_ACTIVE:
6287 case EL_DYNABOMB_PLAYER_4_ACTIVE:
6288 case EL_DYNABOMB_INCREASE_NUMBER:
6289 case EL_DYNABOMB_INCREASE_SIZE:
6290 case EL_DYNABOMB_INCREASE_POWER:
6291 explosion_type = EX_TYPE_DYNA;
6294 case EL_DC_LANDMINE:
6296 case EL_EM_EXIT_OPEN:
6297 case EL_EM_STEEL_EXIT_OPEN:
6299 explosion_type = EX_TYPE_CENTER;
6304 case EL_LAMP_ACTIVE:
6305 case EL_AMOEBA_TO_DIAMOND:
6306 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
6307 explosion_type = EX_TYPE_CENTER;
6311 if (element_info[element].explosion_type == EXPLODES_CROSS)
6312 explosion_type = EX_TYPE_CROSS;
6313 else if (element_info[element].explosion_type == EXPLODES_1X1)
6314 explosion_type = EX_TYPE_CENTER;
6318 if (explosion_type == EX_TYPE_DYNA)
6321 Explode(x, y, EX_PHASE_START, explosion_type);
6323 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6326 void SplashAcid(int x, int y)
6328 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6329 (!IN_LEV_FIELD(x - 1, y - 2) ||
6330 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6331 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6333 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6334 (!IN_LEV_FIELD(x + 1, y - 2) ||
6335 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6336 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6338 PlayLevelSound(x, y, SND_ACID_SPLASHING);
6341 static void InitBeltMovement()
6343 static int belt_base_element[4] =
6345 EL_CONVEYOR_BELT_1_LEFT,
6346 EL_CONVEYOR_BELT_2_LEFT,
6347 EL_CONVEYOR_BELT_3_LEFT,
6348 EL_CONVEYOR_BELT_4_LEFT
6350 static int belt_base_active_element[4] =
6352 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6353 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6354 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6355 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6360 /* set frame order for belt animation graphic according to belt direction */
6361 for (i = 0; i < NUM_BELTS; i++)
6365 for (j = 0; j < NUM_BELT_PARTS; j++)
6367 int element = belt_base_active_element[belt_nr] + j;
6368 int graphic_1 = el2img(element);
6369 int graphic_2 = el2panelimg(element);
6371 if (game.belt_dir[i] == MV_LEFT)
6373 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6374 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6378 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6379 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6384 SCAN_PLAYFIELD(x, y)
6386 int element = Feld[x][y];
6388 for (i = 0; i < NUM_BELTS; i++)
6390 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6392 int e_belt_nr = getBeltNrFromBeltElement(element);
6395 if (e_belt_nr == belt_nr)
6397 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6399 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6406 static void ToggleBeltSwitch(int x, int y)
6408 static int belt_base_element[4] =
6410 EL_CONVEYOR_BELT_1_LEFT,
6411 EL_CONVEYOR_BELT_2_LEFT,
6412 EL_CONVEYOR_BELT_3_LEFT,
6413 EL_CONVEYOR_BELT_4_LEFT
6415 static int belt_base_active_element[4] =
6417 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6418 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6419 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6420 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6422 static int belt_base_switch_element[4] =
6424 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6425 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6426 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6427 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6429 static int belt_move_dir[4] =
6437 int element = Feld[x][y];
6438 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6439 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6440 int belt_dir = belt_move_dir[belt_dir_nr];
6443 if (!IS_BELT_SWITCH(element))
6446 game.belt_dir_nr[belt_nr] = belt_dir_nr;
6447 game.belt_dir[belt_nr] = belt_dir;
6449 if (belt_dir_nr == 3)
6452 /* set frame order for belt animation graphic according to belt direction */
6453 for (i = 0; i < NUM_BELT_PARTS; i++)
6455 int element = belt_base_active_element[belt_nr] + i;
6456 int graphic_1 = el2img(element);
6457 int graphic_2 = el2panelimg(element);
6459 if (belt_dir == MV_LEFT)
6461 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6462 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6466 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6467 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6471 SCAN_PLAYFIELD(xx, yy)
6473 int element = Feld[xx][yy];
6475 if (IS_BELT_SWITCH(element))
6477 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6479 if (e_belt_nr == belt_nr)
6481 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6482 TEST_DrawLevelField(xx, yy);
6485 else if (IS_BELT(element) && belt_dir != MV_NONE)
6487 int e_belt_nr = getBeltNrFromBeltElement(element);
6489 if (e_belt_nr == belt_nr)
6491 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6493 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6494 TEST_DrawLevelField(xx, yy);
6497 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6499 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6501 if (e_belt_nr == belt_nr)
6503 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6505 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6506 TEST_DrawLevelField(xx, yy);
6512 static void ToggleSwitchgateSwitch(int x, int y)
6516 game.switchgate_pos = !game.switchgate_pos;
6518 SCAN_PLAYFIELD(xx, yy)
6520 int element = Feld[xx][yy];
6522 #if !USE_BOTH_SWITCHGATE_SWITCHES
6523 if (element == EL_SWITCHGATE_SWITCH_UP ||
6524 element == EL_SWITCHGATE_SWITCH_DOWN)
6526 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6527 TEST_DrawLevelField(xx, yy);
6529 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6530 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6532 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6533 TEST_DrawLevelField(xx, yy);
6536 if (element == EL_SWITCHGATE_SWITCH_UP)
6538 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6539 TEST_DrawLevelField(xx, yy);
6541 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6543 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6544 TEST_DrawLevelField(xx, yy);
6546 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6548 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6549 TEST_DrawLevelField(xx, yy);
6551 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6553 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6554 TEST_DrawLevelField(xx, yy);
6557 else if (element == EL_SWITCHGATE_OPEN ||
6558 element == EL_SWITCHGATE_OPENING)
6560 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6562 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6564 else if (element == EL_SWITCHGATE_CLOSED ||
6565 element == EL_SWITCHGATE_CLOSING)
6567 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6569 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6574 static int getInvisibleActiveFromInvisibleElement(int element)
6576 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6577 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6578 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6582 static int getInvisibleFromInvisibleActiveElement(int element)
6584 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6585 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6586 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6590 static void RedrawAllLightSwitchesAndInvisibleElements()
6594 SCAN_PLAYFIELD(x, y)
6596 int element = Feld[x][y];
6598 if (element == EL_LIGHT_SWITCH &&
6599 game.light_time_left > 0)
6601 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6602 TEST_DrawLevelField(x, y);
6604 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6605 game.light_time_left == 0)
6607 Feld[x][y] = EL_LIGHT_SWITCH;
6608 TEST_DrawLevelField(x, y);
6610 else if (element == EL_EMC_DRIPPER &&
6611 game.light_time_left > 0)
6613 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6614 TEST_DrawLevelField(x, y);
6616 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6617 game.light_time_left == 0)
6619 Feld[x][y] = EL_EMC_DRIPPER;
6620 TEST_DrawLevelField(x, y);
6622 else if (element == EL_INVISIBLE_STEELWALL ||
6623 element == EL_INVISIBLE_WALL ||
6624 element == EL_INVISIBLE_SAND)
6626 if (game.light_time_left > 0)
6627 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6629 TEST_DrawLevelField(x, y);
6631 /* uncrumble neighbour fields, if needed */
6632 if (element == EL_INVISIBLE_SAND)
6633 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6635 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6636 element == EL_INVISIBLE_WALL_ACTIVE ||
6637 element == EL_INVISIBLE_SAND_ACTIVE)
6639 if (game.light_time_left == 0)
6640 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6642 TEST_DrawLevelField(x, y);
6644 /* re-crumble neighbour fields, if needed */
6645 if (element == EL_INVISIBLE_SAND)
6646 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6651 static void RedrawAllInvisibleElementsForLenses()
6655 SCAN_PLAYFIELD(x, y)
6657 int element = Feld[x][y];
6659 if (element == EL_EMC_DRIPPER &&
6660 game.lenses_time_left > 0)
6662 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6663 TEST_DrawLevelField(x, y);
6665 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6666 game.lenses_time_left == 0)
6668 Feld[x][y] = EL_EMC_DRIPPER;
6669 TEST_DrawLevelField(x, y);
6671 else if (element == EL_INVISIBLE_STEELWALL ||
6672 element == EL_INVISIBLE_WALL ||
6673 element == EL_INVISIBLE_SAND)
6675 if (game.lenses_time_left > 0)
6676 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6678 TEST_DrawLevelField(x, y);
6680 /* uncrumble neighbour fields, if needed */
6681 if (element == EL_INVISIBLE_SAND)
6682 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6684 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6685 element == EL_INVISIBLE_WALL_ACTIVE ||
6686 element == EL_INVISIBLE_SAND_ACTIVE)
6688 if (game.lenses_time_left == 0)
6689 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6691 TEST_DrawLevelField(x, y);
6693 /* re-crumble neighbour fields, if needed */
6694 if (element == EL_INVISIBLE_SAND)
6695 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6700 static void RedrawAllInvisibleElementsForMagnifier()
6704 SCAN_PLAYFIELD(x, y)
6706 int element = Feld[x][y];
6708 if (element == EL_EMC_FAKE_GRASS &&
6709 game.magnify_time_left > 0)
6711 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6712 TEST_DrawLevelField(x, y);
6714 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6715 game.magnify_time_left == 0)
6717 Feld[x][y] = EL_EMC_FAKE_GRASS;
6718 TEST_DrawLevelField(x, y);
6720 else if (IS_GATE_GRAY(element) &&
6721 game.magnify_time_left > 0)
6723 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6724 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6725 IS_EM_GATE_GRAY(element) ?
6726 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6727 IS_EMC_GATE_GRAY(element) ?
6728 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6729 IS_DC_GATE_GRAY(element) ?
6730 EL_DC_GATE_WHITE_GRAY_ACTIVE :
6732 TEST_DrawLevelField(x, y);
6734 else if (IS_GATE_GRAY_ACTIVE(element) &&
6735 game.magnify_time_left == 0)
6737 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6738 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6739 IS_EM_GATE_GRAY_ACTIVE(element) ?
6740 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6741 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6742 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6743 IS_DC_GATE_GRAY_ACTIVE(element) ?
6744 EL_DC_GATE_WHITE_GRAY :
6746 TEST_DrawLevelField(x, y);
6751 static void ToggleLightSwitch(int x, int y)
6753 int element = Feld[x][y];
6755 game.light_time_left =
6756 (element == EL_LIGHT_SWITCH ?
6757 level.time_light * FRAMES_PER_SECOND : 0);
6759 RedrawAllLightSwitchesAndInvisibleElements();
6762 static void ActivateTimegateSwitch(int x, int y)
6766 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6768 SCAN_PLAYFIELD(xx, yy)
6770 int element = Feld[xx][yy];
6772 if (element == EL_TIMEGATE_CLOSED ||
6773 element == EL_TIMEGATE_CLOSING)
6775 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6776 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6780 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6782 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6783 TEST_DrawLevelField(xx, yy);
6790 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6791 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6793 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6797 void Impact(int x, int y)
6799 boolean last_line = (y == lev_fieldy - 1);
6800 boolean object_hit = FALSE;
6801 boolean impact = (last_line || object_hit);
6802 int element = Feld[x][y];
6803 int smashed = EL_STEELWALL;
6805 if (!last_line) /* check if element below was hit */
6807 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6810 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6811 MovDir[x][y + 1] != MV_DOWN ||
6812 MovPos[x][y + 1] <= TILEY / 2));
6814 /* do not smash moving elements that left the smashed field in time */
6815 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6816 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6819 #if USE_QUICKSAND_IMPACT_BUGFIX
6820 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6822 RemoveMovingField(x, y + 1);
6823 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6824 Feld[x][y + 2] = EL_ROCK;
6825 TEST_DrawLevelField(x, y + 2);
6830 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6832 RemoveMovingField(x, y + 1);
6833 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6834 Feld[x][y + 2] = EL_ROCK;
6835 TEST_DrawLevelField(x, y + 2);
6842 smashed = MovingOrBlocked2Element(x, y + 1);
6844 impact = (last_line || object_hit);
6847 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6849 SplashAcid(x, y + 1);
6853 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6854 /* only reset graphic animation if graphic really changes after impact */
6856 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6858 ResetGfxAnimation(x, y);
6859 TEST_DrawLevelField(x, y);
6862 if (impact && CAN_EXPLODE_IMPACT(element))
6867 else if (impact && element == EL_PEARL &&
6868 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6870 ResetGfxAnimation(x, y);
6872 Feld[x][y] = EL_PEARL_BREAKING;
6873 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6876 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6878 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6883 if (impact && element == EL_AMOEBA_DROP)
6885 if (object_hit && IS_PLAYER(x, y + 1))
6886 KillPlayerUnlessEnemyProtected(x, y + 1);
6887 else if (object_hit && smashed == EL_PENGUIN)
6891 Feld[x][y] = EL_AMOEBA_GROWING;
6892 Store[x][y] = EL_AMOEBA_WET;
6894 ResetRandomAnimationValue(x, y);
6899 if (object_hit) /* check which object was hit */
6901 if ((CAN_PASS_MAGIC_WALL(element) &&
6902 (smashed == EL_MAGIC_WALL ||
6903 smashed == EL_BD_MAGIC_WALL)) ||
6904 (CAN_PASS_DC_MAGIC_WALL(element) &&
6905 smashed == EL_DC_MAGIC_WALL))
6908 int activated_magic_wall =
6909 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6910 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6911 EL_DC_MAGIC_WALL_ACTIVE);
6913 /* activate magic wall / mill */
6914 SCAN_PLAYFIELD(xx, yy)
6916 if (Feld[xx][yy] == smashed)
6917 Feld[xx][yy] = activated_magic_wall;
6920 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6921 game.magic_wall_active = TRUE;
6923 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6924 SND_MAGIC_WALL_ACTIVATING :
6925 smashed == EL_BD_MAGIC_WALL ?
6926 SND_BD_MAGIC_WALL_ACTIVATING :
6927 SND_DC_MAGIC_WALL_ACTIVATING));
6930 if (IS_PLAYER(x, y + 1))
6932 if (CAN_SMASH_PLAYER(element))
6934 KillPlayerUnlessEnemyProtected(x, y + 1);
6938 else if (smashed == EL_PENGUIN)
6940 if (CAN_SMASH_PLAYER(element))
6946 else if (element == EL_BD_DIAMOND)
6948 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6954 else if (((element == EL_SP_INFOTRON ||
6955 element == EL_SP_ZONK) &&
6956 (smashed == EL_SP_SNIKSNAK ||
6957 smashed == EL_SP_ELECTRON ||
6958 smashed == EL_SP_DISK_ORANGE)) ||
6959 (element == EL_SP_INFOTRON &&
6960 smashed == EL_SP_DISK_YELLOW))
6965 else if (CAN_SMASH_EVERYTHING(element))
6967 if (IS_CLASSIC_ENEMY(smashed) ||
6968 CAN_EXPLODE_SMASHED(smashed))
6973 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6975 if (smashed == EL_LAMP ||
6976 smashed == EL_LAMP_ACTIVE)
6981 else if (smashed == EL_NUT)
6983 Feld[x][y + 1] = EL_NUT_BREAKING;
6984 PlayLevelSound(x, y, SND_NUT_BREAKING);
6985 RaiseScoreElement(EL_NUT);
6988 else if (smashed == EL_PEARL)
6990 ResetGfxAnimation(x, y);
6992 Feld[x][y + 1] = EL_PEARL_BREAKING;
6993 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6996 else if (smashed == EL_DIAMOND)
6998 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6999 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
7002 else if (IS_BELT_SWITCH(smashed))
7004 ToggleBeltSwitch(x, y + 1);
7006 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
7007 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
7008 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
7009 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
7011 ToggleSwitchgateSwitch(x, y + 1);
7013 else if (smashed == EL_LIGHT_SWITCH ||
7014 smashed == EL_LIGHT_SWITCH_ACTIVE)
7016 ToggleLightSwitch(x, y + 1);
7021 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
7024 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7026 CheckElementChangeBySide(x, y + 1, smashed, element,
7027 CE_SWITCHED, CH_SIDE_TOP);
7028 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7034 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7039 /* play sound of magic wall / mill */
7041 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7042 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7043 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7045 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7046 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7047 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7048 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7049 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7050 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7055 /* play sound of object that hits the ground */
7056 if (last_line || object_hit)
7057 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7060 inline static void TurnRoundExt(int x, int y)
7072 { 0, 0 }, { 0, 0 }, { 0, 0 },
7077 int left, right, back;
7081 { MV_DOWN, MV_UP, MV_RIGHT },
7082 { MV_UP, MV_DOWN, MV_LEFT },
7084 { MV_LEFT, MV_RIGHT, MV_DOWN },
7088 { MV_RIGHT, MV_LEFT, MV_UP }
7091 int element = Feld[x][y];
7092 int move_pattern = element_info[element].move_pattern;
7094 int old_move_dir = MovDir[x][y];
7095 int left_dir = turn[old_move_dir].left;
7096 int right_dir = turn[old_move_dir].right;
7097 int back_dir = turn[old_move_dir].back;
7099 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
7100 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
7101 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
7102 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
7104 int left_x = x + left_dx, left_y = y + left_dy;
7105 int right_x = x + right_dx, right_y = y + right_dy;
7106 int move_x = x + move_dx, move_y = y + move_dy;
7110 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7112 TestIfBadThingTouchesOtherBadThing(x, y);
7114 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7115 MovDir[x][y] = right_dir;
7116 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7117 MovDir[x][y] = left_dir;
7119 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7121 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
7124 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7126 TestIfBadThingTouchesOtherBadThing(x, y);
7128 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7129 MovDir[x][y] = left_dir;
7130 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7131 MovDir[x][y] = right_dir;
7133 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7135 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
7138 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7140 TestIfBadThingTouchesOtherBadThing(x, y);
7142 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7143 MovDir[x][y] = left_dir;
7144 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7145 MovDir[x][y] = right_dir;
7147 if (MovDir[x][y] != old_move_dir)
7150 else if (element == EL_YAMYAM)
7152 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7153 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7155 if (can_turn_left && can_turn_right)
7156 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7157 else if (can_turn_left)
7158 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7159 else if (can_turn_right)
7160 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7162 MovDir[x][y] = back_dir;
7164 MovDelay[x][y] = 16 + 16 * RND(3);
7166 else if (element == EL_DARK_YAMYAM)
7168 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7170 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7173 if (can_turn_left && can_turn_right)
7174 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7175 else if (can_turn_left)
7176 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7177 else if (can_turn_right)
7178 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7180 MovDir[x][y] = back_dir;
7182 MovDelay[x][y] = 16 + 16 * RND(3);
7184 else if (element == EL_PACMAN)
7186 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7187 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7189 if (can_turn_left && can_turn_right)
7190 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7191 else if (can_turn_left)
7192 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7193 else if (can_turn_right)
7194 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7196 MovDir[x][y] = back_dir;
7198 MovDelay[x][y] = 6 + RND(40);
7200 else if (element == EL_PIG)
7202 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7203 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7204 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7205 boolean should_turn_left, should_turn_right, should_move_on;
7207 int rnd = RND(rnd_value);
7209 should_turn_left = (can_turn_left &&
7211 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7212 y + back_dy + left_dy)));
7213 should_turn_right = (can_turn_right &&
7215 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7216 y + back_dy + right_dy)));
7217 should_move_on = (can_move_on &&
7220 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7221 y + move_dy + left_dy) ||
7222 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7223 y + move_dy + right_dy)));
7225 if (should_turn_left || should_turn_right || should_move_on)
7227 if (should_turn_left && should_turn_right && should_move_on)
7228 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
7229 rnd < 2 * rnd_value / 3 ? right_dir :
7231 else if (should_turn_left && should_turn_right)
7232 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7233 else if (should_turn_left && should_move_on)
7234 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7235 else if (should_turn_right && should_move_on)
7236 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7237 else if (should_turn_left)
7238 MovDir[x][y] = left_dir;
7239 else if (should_turn_right)
7240 MovDir[x][y] = right_dir;
7241 else if (should_move_on)
7242 MovDir[x][y] = old_move_dir;
7244 else if (can_move_on && rnd > rnd_value / 8)
7245 MovDir[x][y] = old_move_dir;
7246 else if (can_turn_left && can_turn_right)
7247 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7248 else if (can_turn_left && rnd > rnd_value / 8)
7249 MovDir[x][y] = left_dir;
7250 else if (can_turn_right && rnd > rnd_value/8)
7251 MovDir[x][y] = right_dir;
7253 MovDir[x][y] = back_dir;
7255 xx = x + move_xy[MovDir[x][y]].dx;
7256 yy = y + move_xy[MovDir[x][y]].dy;
7258 if (!IN_LEV_FIELD(xx, yy) ||
7259 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7260 MovDir[x][y] = old_move_dir;
7264 else if (element == EL_DRAGON)
7266 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7267 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7268 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7270 int rnd = RND(rnd_value);
7272 if (can_move_on && rnd > rnd_value / 8)
7273 MovDir[x][y] = old_move_dir;
7274 else if (can_turn_left && can_turn_right)
7275 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7276 else if (can_turn_left && rnd > rnd_value / 8)
7277 MovDir[x][y] = left_dir;
7278 else if (can_turn_right && rnd > rnd_value / 8)
7279 MovDir[x][y] = right_dir;
7281 MovDir[x][y] = back_dir;
7283 xx = x + move_xy[MovDir[x][y]].dx;
7284 yy = y + move_xy[MovDir[x][y]].dy;
7286 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7287 MovDir[x][y] = old_move_dir;
7291 else if (element == EL_MOLE)
7293 boolean can_move_on =
7294 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7295 IS_AMOEBOID(Feld[move_x][move_y]) ||
7296 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7299 boolean can_turn_left =
7300 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7301 IS_AMOEBOID(Feld[left_x][left_y])));
7303 boolean can_turn_right =
7304 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7305 IS_AMOEBOID(Feld[right_x][right_y])));
7307 if (can_turn_left && can_turn_right)
7308 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7309 else if (can_turn_left)
7310 MovDir[x][y] = left_dir;
7312 MovDir[x][y] = right_dir;
7315 if (MovDir[x][y] != old_move_dir)
7318 else if (element == EL_BALLOON)
7320 MovDir[x][y] = game.wind_direction;
7323 else if (element == EL_SPRING)
7325 #if USE_NEW_SPRING_BUMPER
7326 if (MovDir[x][y] & MV_HORIZONTAL)
7328 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7329 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7331 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7332 ResetGfxAnimation(move_x, move_y);
7333 TEST_DrawLevelField(move_x, move_y);
7335 MovDir[x][y] = back_dir;
7337 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7338 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7339 MovDir[x][y] = MV_NONE;
7342 if (MovDir[x][y] & MV_HORIZONTAL &&
7343 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7344 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7345 MovDir[x][y] = MV_NONE;
7350 else if (element == EL_ROBOT ||
7351 element == EL_SATELLITE ||
7352 element == EL_PENGUIN ||
7353 element == EL_EMC_ANDROID)
7355 int attr_x = -1, attr_y = -1;
7366 for (i = 0; i < MAX_PLAYERS; i++)
7368 struct PlayerInfo *player = &stored_player[i];
7369 int jx = player->jx, jy = player->jy;
7371 if (!player->active)
7375 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7383 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7384 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7385 game.engine_version < VERSION_IDENT(3,1,0,0)))
7391 if (element == EL_PENGUIN)
7394 static int xy[4][2] =
7402 for (i = 0; i < NUM_DIRECTIONS; i++)
7404 int ex = x + xy[i][0];
7405 int ey = y + xy[i][1];
7407 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7408 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7409 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7410 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7419 MovDir[x][y] = MV_NONE;
7421 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7422 else if (attr_x > x)
7423 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7425 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7426 else if (attr_y > y)
7427 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7429 if (element == EL_ROBOT)
7433 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7434 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7435 Moving2Blocked(x, y, &newx, &newy);
7437 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7438 MovDelay[x][y] = 8 + 8 * !RND(3);
7440 MovDelay[x][y] = 16;
7442 else if (element == EL_PENGUIN)
7448 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7450 boolean first_horiz = RND(2);
7451 int new_move_dir = MovDir[x][y];
7454 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7455 Moving2Blocked(x, y, &newx, &newy);
7457 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7461 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7462 Moving2Blocked(x, y, &newx, &newy);
7464 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7467 MovDir[x][y] = old_move_dir;
7471 else if (element == EL_SATELLITE)
7477 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7479 boolean first_horiz = RND(2);
7480 int new_move_dir = MovDir[x][y];
7483 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7484 Moving2Blocked(x, y, &newx, &newy);
7486 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7490 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7491 Moving2Blocked(x, y, &newx, &newy);
7493 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7496 MovDir[x][y] = old_move_dir;
7500 else if (element == EL_EMC_ANDROID)
7502 static int check_pos[16] =
7504 -1, /* 0 => (invalid) */
7505 7, /* 1 => MV_LEFT */
7506 3, /* 2 => MV_RIGHT */
7507 -1, /* 3 => (invalid) */
7509 0, /* 5 => MV_LEFT | MV_UP */
7510 2, /* 6 => MV_RIGHT | MV_UP */
7511 -1, /* 7 => (invalid) */
7512 5, /* 8 => MV_DOWN */
7513 6, /* 9 => MV_LEFT | MV_DOWN */
7514 4, /* 10 => MV_RIGHT | MV_DOWN */
7515 -1, /* 11 => (invalid) */
7516 -1, /* 12 => (invalid) */
7517 -1, /* 13 => (invalid) */
7518 -1, /* 14 => (invalid) */
7519 -1, /* 15 => (invalid) */
7527 { -1, -1, MV_LEFT | MV_UP },
7529 { +1, -1, MV_RIGHT | MV_UP },
7530 { +1, 0, MV_RIGHT },
7531 { +1, +1, MV_RIGHT | MV_DOWN },
7533 { -1, +1, MV_LEFT | MV_DOWN },
7536 int start_pos, check_order;
7537 boolean can_clone = FALSE;
7540 /* check if there is any free field around current position */
7541 for (i = 0; i < 8; i++)
7543 int newx = x + check_xy[i].dx;
7544 int newy = y + check_xy[i].dy;
7546 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7554 if (can_clone) /* randomly find an element to clone */
7558 start_pos = check_pos[RND(8)];
7559 check_order = (RND(2) ? -1 : +1);
7561 for (i = 0; i < 8; i++)
7563 int pos_raw = start_pos + i * check_order;
7564 int pos = (pos_raw + 8) % 8;
7565 int newx = x + check_xy[pos].dx;
7566 int newy = y + check_xy[pos].dy;
7568 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7570 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7571 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7573 Store[x][y] = Feld[newx][newy];
7582 if (can_clone) /* randomly find a direction to move */
7586 start_pos = check_pos[RND(8)];
7587 check_order = (RND(2) ? -1 : +1);
7589 for (i = 0; i < 8; i++)
7591 int pos_raw = start_pos + i * check_order;
7592 int pos = (pos_raw + 8) % 8;
7593 int newx = x + check_xy[pos].dx;
7594 int newy = y + check_xy[pos].dy;
7595 int new_move_dir = check_xy[pos].dir;
7597 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7599 MovDir[x][y] = new_move_dir;
7600 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7609 if (can_clone) /* cloning and moving successful */
7612 /* cannot clone -- try to move towards player */
7614 start_pos = check_pos[MovDir[x][y] & 0x0f];
7615 check_order = (RND(2) ? -1 : +1);
7617 for (i = 0; i < 3; i++)
7619 /* first check start_pos, then previous/next or (next/previous) pos */
7620 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7621 int pos = (pos_raw + 8) % 8;
7622 int newx = x + check_xy[pos].dx;
7623 int newy = y + check_xy[pos].dy;
7624 int new_move_dir = check_xy[pos].dir;
7626 if (IS_PLAYER(newx, newy))
7629 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7631 MovDir[x][y] = new_move_dir;
7632 MovDelay[x][y] = level.android_move_time * 8 + 1;
7639 else if (move_pattern == MV_TURNING_LEFT ||
7640 move_pattern == MV_TURNING_RIGHT ||
7641 move_pattern == MV_TURNING_LEFT_RIGHT ||
7642 move_pattern == MV_TURNING_RIGHT_LEFT ||
7643 move_pattern == MV_TURNING_RANDOM ||
7644 move_pattern == MV_ALL_DIRECTIONS)
7646 boolean can_turn_left =
7647 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7648 boolean can_turn_right =
7649 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7651 if (element_info[element].move_stepsize == 0) /* "not moving" */
7654 if (move_pattern == MV_TURNING_LEFT)
7655 MovDir[x][y] = left_dir;
7656 else if (move_pattern == MV_TURNING_RIGHT)
7657 MovDir[x][y] = right_dir;
7658 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7659 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7660 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7661 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7662 else if (move_pattern == MV_TURNING_RANDOM)
7663 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7664 can_turn_right && !can_turn_left ? right_dir :
7665 RND(2) ? left_dir : right_dir);
7666 else if (can_turn_left && can_turn_right)
7667 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7668 else if (can_turn_left)
7669 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7670 else if (can_turn_right)
7671 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7673 MovDir[x][y] = back_dir;
7675 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7677 else if (move_pattern == MV_HORIZONTAL ||
7678 move_pattern == MV_VERTICAL)
7680 if (move_pattern & old_move_dir)
7681 MovDir[x][y] = back_dir;
7682 else if (move_pattern == MV_HORIZONTAL)
7683 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7684 else if (move_pattern == MV_VERTICAL)
7685 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7687 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7689 else if (move_pattern & MV_ANY_DIRECTION)
7691 MovDir[x][y] = move_pattern;
7692 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7694 else if (move_pattern & MV_WIND_DIRECTION)
7696 MovDir[x][y] = game.wind_direction;
7697 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7699 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7701 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7702 MovDir[x][y] = left_dir;
7703 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7704 MovDir[x][y] = right_dir;
7706 if (MovDir[x][y] != old_move_dir)
7707 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7709 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7711 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7712 MovDir[x][y] = right_dir;
7713 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7714 MovDir[x][y] = left_dir;
7716 if (MovDir[x][y] != old_move_dir)
7717 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7719 else if (move_pattern == MV_TOWARDS_PLAYER ||
7720 move_pattern == MV_AWAY_FROM_PLAYER)
7722 int attr_x = -1, attr_y = -1;
7724 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7735 for (i = 0; i < MAX_PLAYERS; i++)
7737 struct PlayerInfo *player = &stored_player[i];
7738 int jx = player->jx, jy = player->jy;
7740 if (!player->active)
7744 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7752 MovDir[x][y] = MV_NONE;
7754 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7755 else if (attr_x > x)
7756 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7758 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7759 else if (attr_y > y)
7760 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7762 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7764 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7766 boolean first_horiz = RND(2);
7767 int new_move_dir = MovDir[x][y];
7769 if (element_info[element].move_stepsize == 0) /* "not moving" */
7771 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7772 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7778 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7779 Moving2Blocked(x, y, &newx, &newy);
7781 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7785 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7786 Moving2Blocked(x, y, &newx, &newy);
7788 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7791 MovDir[x][y] = old_move_dir;
7794 else if (move_pattern == MV_WHEN_PUSHED ||
7795 move_pattern == MV_WHEN_DROPPED)
7797 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7798 MovDir[x][y] = MV_NONE;
7802 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7804 static int test_xy[7][2] =
7814 static int test_dir[7] =
7824 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7825 int move_preference = -1000000; /* start with very low preference */
7826 int new_move_dir = MV_NONE;
7827 int start_test = RND(4);
7830 for (i = 0; i < NUM_DIRECTIONS; i++)
7832 int move_dir = test_dir[start_test + i];
7833 int move_dir_preference;
7835 xx = x + test_xy[start_test + i][0];
7836 yy = y + test_xy[start_test + i][1];
7838 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7839 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7841 new_move_dir = move_dir;
7846 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7849 move_dir_preference = -1 * RunnerVisit[xx][yy];
7850 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7851 move_dir_preference = PlayerVisit[xx][yy];
7853 if (move_dir_preference > move_preference)
7855 /* prefer field that has not been visited for the longest time */
7856 move_preference = move_dir_preference;
7857 new_move_dir = move_dir;
7859 else if (move_dir_preference == move_preference &&
7860 move_dir == old_move_dir)
7862 /* prefer last direction when all directions are preferred equally */
7863 move_preference = move_dir_preference;
7864 new_move_dir = move_dir;
7868 MovDir[x][y] = new_move_dir;
7869 if (old_move_dir != new_move_dir)
7870 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7874 static void TurnRound(int x, int y)
7876 int direction = MovDir[x][y];
7880 GfxDir[x][y] = MovDir[x][y];
7882 if (direction != MovDir[x][y])
7886 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7888 ResetGfxFrame(x, y, FALSE);
7891 static boolean JustBeingPushed(int x, int y)
7895 for (i = 0; i < MAX_PLAYERS; i++)
7897 struct PlayerInfo *player = &stored_player[i];
7899 if (player->active && player->is_pushing && player->MovPos)
7901 int next_jx = player->jx + (player->jx - player->last_jx);
7902 int next_jy = player->jy + (player->jy - player->last_jy);
7904 if (x == next_jx && y == next_jy)
7912 void StartMoving(int x, int y)
7914 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7915 int element = Feld[x][y];
7920 if (MovDelay[x][y] == 0)
7921 GfxAction[x][y] = ACTION_DEFAULT;
7923 if (CAN_FALL(element) && y < lev_fieldy - 1)
7925 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7926 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7927 if (JustBeingPushed(x, y))
7930 if (element == EL_QUICKSAND_FULL)
7932 if (IS_FREE(x, y + 1))
7934 InitMovingField(x, y, MV_DOWN);
7935 started_moving = TRUE;
7937 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7938 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7939 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7940 Store[x][y] = EL_ROCK;
7942 Store[x][y] = EL_ROCK;
7945 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7947 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7949 if (!MovDelay[x][y])
7951 MovDelay[x][y] = TILEY + 1;
7953 ResetGfxAnimation(x, y);
7954 ResetGfxAnimation(x, y + 1);
7959 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7960 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7967 Feld[x][y] = EL_QUICKSAND_EMPTY;
7968 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7969 Store[x][y + 1] = Store[x][y];
7972 PlayLevelSoundAction(x, y, ACTION_FILLING);
7974 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7976 if (!MovDelay[x][y])
7978 MovDelay[x][y] = TILEY + 1;
7980 ResetGfxAnimation(x, y);
7981 ResetGfxAnimation(x, y + 1);
7986 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7987 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7994 Feld[x][y] = EL_QUICKSAND_EMPTY;
7995 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7996 Store[x][y + 1] = Store[x][y];
7999 PlayLevelSoundAction(x, y, ACTION_FILLING);
8002 else if (element == EL_QUICKSAND_FAST_FULL)
8004 if (IS_FREE(x, y + 1))
8006 InitMovingField(x, y, MV_DOWN);
8007 started_moving = TRUE;
8009 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
8010 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8011 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8012 Store[x][y] = EL_ROCK;
8014 Store[x][y] = EL_ROCK;
8017 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8019 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8021 if (!MovDelay[x][y])
8023 MovDelay[x][y] = TILEY + 1;
8025 ResetGfxAnimation(x, y);
8026 ResetGfxAnimation(x, y + 1);
8031 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8032 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8039 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8040 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8041 Store[x][y + 1] = Store[x][y];
8044 PlayLevelSoundAction(x, y, ACTION_FILLING);
8046 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8048 if (!MovDelay[x][y])
8050 MovDelay[x][y] = TILEY + 1;
8052 ResetGfxAnimation(x, y);
8053 ResetGfxAnimation(x, y + 1);
8058 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8059 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8066 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8067 Feld[x][y + 1] = EL_QUICKSAND_FULL;
8068 Store[x][y + 1] = Store[x][y];
8071 PlayLevelSoundAction(x, y, ACTION_FILLING);
8074 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8075 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8077 InitMovingField(x, y, MV_DOWN);
8078 started_moving = TRUE;
8080 Feld[x][y] = EL_QUICKSAND_FILLING;
8081 Store[x][y] = element;
8083 PlayLevelSoundAction(x, y, ACTION_FILLING);
8085 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8086 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8088 InitMovingField(x, y, MV_DOWN);
8089 started_moving = TRUE;
8091 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
8092 Store[x][y] = element;
8094 PlayLevelSoundAction(x, y, ACTION_FILLING);
8096 else if (element == EL_MAGIC_WALL_FULL)
8098 if (IS_FREE(x, y + 1))
8100 InitMovingField(x, y, MV_DOWN);
8101 started_moving = TRUE;
8103 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
8104 Store[x][y] = EL_CHANGED(Store[x][y]);
8106 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8108 if (!MovDelay[x][y])
8109 MovDelay[x][y] = TILEY/4 + 1;
8118 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
8119 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
8120 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8124 else if (element == EL_BD_MAGIC_WALL_FULL)
8126 if (IS_FREE(x, y + 1))
8128 InitMovingField(x, y, MV_DOWN);
8129 started_moving = TRUE;
8131 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8132 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8134 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8136 if (!MovDelay[x][y])
8137 MovDelay[x][y] = TILEY/4 + 1;
8146 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8147 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8148 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8152 else if (element == EL_DC_MAGIC_WALL_FULL)
8154 if (IS_FREE(x, y + 1))
8156 InitMovingField(x, y, MV_DOWN);
8157 started_moving = TRUE;
8159 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8160 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8162 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8164 if (!MovDelay[x][y])
8165 MovDelay[x][y] = TILEY/4 + 1;
8174 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8175 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8176 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8180 else if ((CAN_PASS_MAGIC_WALL(element) &&
8181 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8182 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8183 (CAN_PASS_DC_MAGIC_WALL(element) &&
8184 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8187 InitMovingField(x, y, MV_DOWN);
8188 started_moving = TRUE;
8191 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8192 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8193 EL_DC_MAGIC_WALL_FILLING);
8194 Store[x][y] = element;
8196 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8198 SplashAcid(x, y + 1);
8200 InitMovingField(x, y, MV_DOWN);
8201 started_moving = TRUE;
8203 Store[x][y] = EL_ACID;
8206 #if USE_FIX_IMPACT_COLLISION
8207 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8208 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8210 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8211 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8213 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8214 CAN_FALL(element) && WasJustFalling[x][y] &&
8215 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8217 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8218 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8219 (Feld[x][y + 1] == EL_BLOCKED)))
8221 /* this is needed for a special case not covered by calling "Impact()"
8222 from "ContinueMoving()": if an element moves to a tile directly below
8223 another element which was just falling on that tile (which was empty
8224 in the previous frame), the falling element above would just stop
8225 instead of smashing the element below (in previous version, the above
8226 element was just checked for "moving" instead of "falling", resulting
8227 in incorrect smashes caused by horizontal movement of the above
8228 element; also, the case of the player being the element to smash was
8229 simply not covered here... :-/ ) */
8231 CheckCollision[x][y] = 0;
8232 CheckImpact[x][y] = 0;
8236 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8238 if (MovDir[x][y] == MV_NONE)
8240 InitMovingField(x, y, MV_DOWN);
8241 started_moving = TRUE;
8244 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8246 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8247 MovDir[x][y] = MV_DOWN;
8249 InitMovingField(x, y, MV_DOWN);
8250 started_moving = TRUE;
8252 else if (element == EL_AMOEBA_DROP)
8254 Feld[x][y] = EL_AMOEBA_GROWING;
8255 Store[x][y] = EL_AMOEBA_WET;
8257 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8258 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8259 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8260 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8262 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
8263 (IS_FREE(x - 1, y + 1) ||
8264 Feld[x - 1][y + 1] == EL_ACID));
8265 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8266 (IS_FREE(x + 1, y + 1) ||
8267 Feld[x + 1][y + 1] == EL_ACID));
8268 boolean can_fall_any = (can_fall_left || can_fall_right);
8269 boolean can_fall_both = (can_fall_left && can_fall_right);
8270 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8272 #if USE_NEW_ALL_SLIPPERY
8273 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8275 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8276 can_fall_right = FALSE;
8277 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8278 can_fall_left = FALSE;
8279 else if (slippery_type == SLIPPERY_ONLY_LEFT)
8280 can_fall_right = FALSE;
8281 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8282 can_fall_left = FALSE;
8284 can_fall_any = (can_fall_left || can_fall_right);
8285 can_fall_both = FALSE;
8288 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8290 if (slippery_type == SLIPPERY_ONLY_LEFT)
8291 can_fall_right = FALSE;
8292 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8293 can_fall_left = FALSE;
8294 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8295 can_fall_right = FALSE;
8296 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8297 can_fall_left = FALSE;
8299 can_fall_any = (can_fall_left || can_fall_right);
8300 can_fall_both = (can_fall_left && can_fall_right);
8304 #if USE_NEW_ALL_SLIPPERY
8306 #if USE_NEW_SP_SLIPPERY
8307 /* !!! better use the same properties as for custom elements here !!! */
8308 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8309 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8311 can_fall_right = FALSE; /* slip down on left side */
8312 can_fall_both = FALSE;
8317 #if USE_NEW_ALL_SLIPPERY
8320 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8321 can_fall_right = FALSE; /* slip down on left side */
8323 can_fall_left = !(can_fall_right = RND(2));
8325 can_fall_both = FALSE;
8330 if (game.emulation == EMU_BOULDERDASH ||
8331 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8332 can_fall_right = FALSE; /* slip down on left side */
8334 can_fall_left = !(can_fall_right = RND(2));
8336 can_fall_both = FALSE;
8342 /* if not determined otherwise, prefer left side for slipping down */
8343 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8344 started_moving = TRUE;
8348 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8350 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8353 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
8354 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8355 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8356 int belt_dir = game.belt_dir[belt_nr];
8358 if ((belt_dir == MV_LEFT && left_is_free) ||
8359 (belt_dir == MV_RIGHT && right_is_free))
8361 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8363 InitMovingField(x, y, belt_dir);
8364 started_moving = TRUE;
8366 Pushed[x][y] = TRUE;
8367 Pushed[nextx][y] = TRUE;
8369 GfxAction[x][y] = ACTION_DEFAULT;
8373 MovDir[x][y] = 0; /* if element was moving, stop it */
8378 /* not "else if" because of elements that can fall and move (EL_SPRING) */
8380 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8382 if (CAN_MOVE(element) && !started_moving)
8385 int move_pattern = element_info[element].move_pattern;
8390 if (MovDir[x][y] == MV_NONE)
8392 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8393 x, y, element, element_info[element].token_name);
8394 printf("StartMoving(): This should never happen!\n");
8399 Moving2Blocked(x, y, &newx, &newy);
8401 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8404 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8405 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8407 WasJustMoving[x][y] = 0;
8408 CheckCollision[x][y] = 0;
8410 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8412 if (Feld[x][y] != element) /* element has changed */
8416 if (!MovDelay[x][y]) /* start new movement phase */
8418 /* all objects that can change their move direction after each step
8419 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8421 if (element != EL_YAMYAM &&
8422 element != EL_DARK_YAMYAM &&
8423 element != EL_PACMAN &&
8424 !(move_pattern & MV_ANY_DIRECTION) &&
8425 move_pattern != MV_TURNING_LEFT &&
8426 move_pattern != MV_TURNING_RIGHT &&
8427 move_pattern != MV_TURNING_LEFT_RIGHT &&
8428 move_pattern != MV_TURNING_RIGHT_LEFT &&
8429 move_pattern != MV_TURNING_RANDOM)
8433 if (MovDelay[x][y] && (element == EL_BUG ||
8434 element == EL_SPACESHIP ||
8435 element == EL_SP_SNIKSNAK ||
8436 element == EL_SP_ELECTRON ||
8437 element == EL_MOLE))
8438 TEST_DrawLevelField(x, y);
8442 if (MovDelay[x][y]) /* wait some time before next movement */
8446 if (element == EL_ROBOT ||
8447 element == EL_YAMYAM ||
8448 element == EL_DARK_YAMYAM)
8450 DrawLevelElementAnimationIfNeeded(x, y, element);
8451 PlayLevelSoundAction(x, y, ACTION_WAITING);
8453 else if (element == EL_SP_ELECTRON)
8454 DrawLevelElementAnimationIfNeeded(x, y, element);
8455 else if (element == EL_DRAGON)
8458 int dir = MovDir[x][y];
8459 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8460 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
8461 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
8462 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
8463 dir == MV_UP ? IMG_FLAMES_1_UP :
8464 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8465 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8467 GfxAction[x][y] = ACTION_ATTACKING;
8469 if (IS_PLAYER(x, y))
8470 DrawPlayerField(x, y);
8472 TEST_DrawLevelField(x, y);
8474 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8476 for (i = 1; i <= 3; i++)
8478 int xx = x + i * dx;
8479 int yy = y + i * dy;
8480 int sx = SCREENX(xx);
8481 int sy = SCREENY(yy);
8482 int flame_graphic = graphic + (i - 1);
8484 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8489 int flamed = MovingOrBlocked2Element(xx, yy);
8493 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8495 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8496 RemoveMovingField(xx, yy);
8498 RemoveField(xx, yy);
8500 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8503 RemoveMovingField(xx, yy);
8506 ChangeDelay[xx][yy] = 0;
8508 Feld[xx][yy] = EL_FLAMES;
8510 if (IN_SCR_FIELD(sx, sy))
8512 TEST_DrawLevelFieldCrumbled(xx, yy);
8513 DrawGraphic(sx, sy, flame_graphic, frame);
8518 if (Feld[xx][yy] == EL_FLAMES)
8519 Feld[xx][yy] = EL_EMPTY;
8520 TEST_DrawLevelField(xx, yy);
8525 if (MovDelay[x][y]) /* element still has to wait some time */
8527 PlayLevelSoundAction(x, y, ACTION_WAITING);
8533 /* now make next step */
8535 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8537 if (DONT_COLLIDE_WITH(element) &&
8538 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8539 !PLAYER_ENEMY_PROTECTED(newx, newy))
8541 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8546 else if (CAN_MOVE_INTO_ACID(element) &&
8547 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8548 !IS_MV_DIAGONAL(MovDir[x][y]) &&
8549 (MovDir[x][y] == MV_DOWN ||
8550 game.engine_version >= VERSION_IDENT(3,1,0,0)))
8552 SplashAcid(newx, newy);
8553 Store[x][y] = EL_ACID;
8555 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8557 if (Feld[newx][newy] == EL_EXIT_OPEN ||
8558 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8559 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8560 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8563 TEST_DrawLevelField(x, y);
8565 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8566 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8567 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8569 local_player->friends_still_needed--;
8570 if (!local_player->friends_still_needed &&
8571 !local_player->GameOver && AllPlayersGone)
8572 PlayerWins(local_player);
8576 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8578 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8579 TEST_DrawLevelField(newx, newy);
8581 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8583 else if (!IS_FREE(newx, newy))
8585 GfxAction[x][y] = ACTION_WAITING;
8587 if (IS_PLAYER(x, y))
8588 DrawPlayerField(x, y);
8590 TEST_DrawLevelField(x, y);
8595 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8597 if (IS_FOOD_PIG(Feld[newx][newy]))
8599 if (IS_MOVING(newx, newy))
8600 RemoveMovingField(newx, newy);
8603 Feld[newx][newy] = EL_EMPTY;
8604 TEST_DrawLevelField(newx, newy);
8607 PlayLevelSound(x, y, SND_PIG_DIGGING);
8609 else if (!IS_FREE(newx, newy))
8611 if (IS_PLAYER(x, y))
8612 DrawPlayerField(x, y);
8614 TEST_DrawLevelField(x, y);
8619 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8621 if (Store[x][y] != EL_EMPTY)
8623 boolean can_clone = FALSE;
8626 /* check if element to clone is still there */
8627 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8629 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8637 /* cannot clone or target field not free anymore -- do not clone */
8638 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8639 Store[x][y] = EL_EMPTY;
8642 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8644 if (IS_MV_DIAGONAL(MovDir[x][y]))
8646 int diagonal_move_dir = MovDir[x][y];
8647 int stored = Store[x][y];
8648 int change_delay = 8;
8651 /* android is moving diagonally */
8653 CreateField(x, y, EL_DIAGONAL_SHRINKING);
8655 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8656 GfxElement[x][y] = EL_EMC_ANDROID;
8657 GfxAction[x][y] = ACTION_SHRINKING;
8658 GfxDir[x][y] = diagonal_move_dir;
8659 ChangeDelay[x][y] = change_delay;
8661 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8664 DrawLevelGraphicAnimation(x, y, graphic);
8665 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8667 if (Feld[newx][newy] == EL_ACID)
8669 SplashAcid(newx, newy);
8674 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8676 Store[newx][newy] = EL_EMC_ANDROID;
8677 GfxElement[newx][newy] = EL_EMC_ANDROID;
8678 GfxAction[newx][newy] = ACTION_GROWING;
8679 GfxDir[newx][newy] = diagonal_move_dir;
8680 ChangeDelay[newx][newy] = change_delay;
8682 graphic = el_act_dir2img(GfxElement[newx][newy],
8683 GfxAction[newx][newy], GfxDir[newx][newy]);
8685 DrawLevelGraphicAnimation(newx, newy, graphic);
8686 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8692 Feld[newx][newy] = EL_EMPTY;
8693 TEST_DrawLevelField(newx, newy);
8695 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8698 else if (!IS_FREE(newx, newy))
8701 if (IS_PLAYER(x, y))
8702 DrawPlayerField(x, y);
8704 TEST_DrawLevelField(x, y);
8710 else if (IS_CUSTOM_ELEMENT(element) &&
8711 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8714 if (!DigFieldByCE(newx, newy, element))
8717 int new_element = Feld[newx][newy];
8719 if (!IS_FREE(newx, newy))
8721 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8722 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8725 /* no element can dig solid indestructible elements */
8726 if (IS_INDESTRUCTIBLE(new_element) &&
8727 !IS_DIGGABLE(new_element) &&
8728 !IS_COLLECTIBLE(new_element))
8731 if (AmoebaNr[newx][newy] &&
8732 (new_element == EL_AMOEBA_FULL ||
8733 new_element == EL_BD_AMOEBA ||
8734 new_element == EL_AMOEBA_GROWING))
8736 AmoebaCnt[AmoebaNr[newx][newy]]--;
8737 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8740 if (IS_MOVING(newx, newy))
8741 RemoveMovingField(newx, newy);
8744 RemoveField(newx, newy);
8745 TEST_DrawLevelField(newx, newy);
8748 /* if digged element was about to explode, prevent the explosion */
8749 ExplodeField[newx][newy] = EX_TYPE_NONE;
8751 PlayLevelSoundAction(x, y, action);
8754 Store[newx][newy] = EL_EMPTY;
8757 /* this makes it possible to leave the removed element again */
8758 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8759 Store[newx][newy] = new_element;
8761 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8763 int move_leave_element = element_info[element].move_leave_element;
8765 /* this makes it possible to leave the removed element again */
8766 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8767 new_element : move_leave_element);
8773 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8775 RunnerVisit[x][y] = FrameCounter;
8776 PlayerVisit[x][y] /= 8; /* expire player visit path */
8779 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8781 if (!IS_FREE(newx, newy))
8783 if (IS_PLAYER(x, y))
8784 DrawPlayerField(x, y);
8786 TEST_DrawLevelField(x, y);
8792 boolean wanna_flame = !RND(10);
8793 int dx = newx - x, dy = newy - y;
8794 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8795 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8796 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8797 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8798 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8799 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8802 IS_CLASSIC_ENEMY(element1) ||
8803 IS_CLASSIC_ENEMY(element2)) &&
8804 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8805 element1 != EL_FLAMES && element2 != EL_FLAMES)
8807 ResetGfxAnimation(x, y);
8808 GfxAction[x][y] = ACTION_ATTACKING;
8810 if (IS_PLAYER(x, y))
8811 DrawPlayerField(x, y);
8813 TEST_DrawLevelField(x, y);
8815 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8817 MovDelay[x][y] = 50;
8821 RemoveField(newx, newy);
8823 Feld[newx][newy] = EL_FLAMES;
8824 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8827 RemoveField(newx1, newy1);
8829 Feld[newx1][newy1] = EL_FLAMES;
8831 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8834 RemoveField(newx2, newy2);
8836 Feld[newx2][newy2] = EL_FLAMES;
8843 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8844 Feld[newx][newy] == EL_DIAMOND)
8846 if (IS_MOVING(newx, newy))
8847 RemoveMovingField(newx, newy);
8850 Feld[newx][newy] = EL_EMPTY;
8851 TEST_DrawLevelField(newx, newy);
8854 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8856 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8857 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8859 if (AmoebaNr[newx][newy])
8861 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8862 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8863 Feld[newx][newy] == EL_BD_AMOEBA)
8864 AmoebaCnt[AmoebaNr[newx][newy]]--;
8869 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8871 RemoveMovingField(newx, newy);
8874 if (IS_MOVING(newx, newy))
8876 RemoveMovingField(newx, newy);
8881 Feld[newx][newy] = EL_EMPTY;
8882 TEST_DrawLevelField(newx, newy);
8885 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8887 else if ((element == EL_PACMAN || element == EL_MOLE)
8888 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8890 if (AmoebaNr[newx][newy])
8892 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8893 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8894 Feld[newx][newy] == EL_BD_AMOEBA)
8895 AmoebaCnt[AmoebaNr[newx][newy]]--;
8898 if (element == EL_MOLE)
8900 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8901 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8903 ResetGfxAnimation(x, y);
8904 GfxAction[x][y] = ACTION_DIGGING;
8905 TEST_DrawLevelField(x, y);
8907 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8909 return; /* wait for shrinking amoeba */
8911 else /* element == EL_PACMAN */
8913 Feld[newx][newy] = EL_EMPTY;
8914 TEST_DrawLevelField(newx, newy);
8915 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8918 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8919 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8920 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8922 /* wait for shrinking amoeba to completely disappear */
8925 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8927 /* object was running against a wall */
8932 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8933 if (move_pattern & MV_ANY_DIRECTION &&
8934 move_pattern == MovDir[x][y])
8936 int blocking_element =
8937 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8939 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8942 element = Feld[x][y]; /* element might have changed */
8946 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8947 DrawLevelElementAnimation(x, y, element);
8949 if (DONT_TOUCH(element))
8950 TestIfBadThingTouchesPlayer(x, y);
8955 InitMovingField(x, y, MovDir[x][y]);
8957 PlayLevelSoundAction(x, y, ACTION_MOVING);
8961 ContinueMoving(x, y);
8964 void ContinueMoving(int x, int y)
8966 int element = Feld[x][y];
8967 struct ElementInfo *ei = &element_info[element];
8968 int direction = MovDir[x][y];
8969 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8970 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8971 int newx = x + dx, newy = y + dy;
8972 int stored = Store[x][y];
8973 int stored_new = Store[newx][newy];
8974 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
8975 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8976 boolean last_line = (newy == lev_fieldy - 1);
8978 MovPos[x][y] += getElementMoveStepsize(x, y);
8980 if (pushed_by_player) /* special case: moving object pushed by player */
8981 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8983 if (ABS(MovPos[x][y]) < TILEX)
8986 int ee = Feld[x][y];
8987 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8988 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8990 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8991 x, y, ABS(MovPos[x][y]),
8993 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8996 TEST_DrawLevelField(x, y);
8998 return; /* element is still moving */
9001 /* element reached destination field */
9003 Feld[x][y] = EL_EMPTY;
9004 Feld[newx][newy] = element;
9005 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
9007 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
9009 element = Feld[newx][newy] = EL_ACID;
9011 else if (element == EL_MOLE)
9013 Feld[x][y] = EL_SAND;
9015 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9017 else if (element == EL_QUICKSAND_FILLING)
9019 element = Feld[newx][newy] = get_next_element(element);
9020 Store[newx][newy] = Store[x][y];
9022 else if (element == EL_QUICKSAND_EMPTYING)
9024 Feld[x][y] = get_next_element(element);
9025 element = Feld[newx][newy] = Store[x][y];
9027 else if (element == EL_QUICKSAND_FAST_FILLING)
9029 element = Feld[newx][newy] = get_next_element(element);
9030 Store[newx][newy] = Store[x][y];
9032 else if (element == EL_QUICKSAND_FAST_EMPTYING)
9034 Feld[x][y] = get_next_element(element);
9035 element = Feld[newx][newy] = Store[x][y];
9037 else if (element == EL_MAGIC_WALL_FILLING)
9039 element = Feld[newx][newy] = get_next_element(element);
9040 if (!game.magic_wall_active)
9041 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
9042 Store[newx][newy] = Store[x][y];
9044 else if (element == EL_MAGIC_WALL_EMPTYING)
9046 Feld[x][y] = get_next_element(element);
9047 if (!game.magic_wall_active)
9048 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9049 element = Feld[newx][newy] = Store[x][y];
9051 #if USE_NEW_CUSTOM_VALUE
9052 InitField(newx, newy, FALSE);
9055 else if (element == EL_BD_MAGIC_WALL_FILLING)
9057 element = Feld[newx][newy] = get_next_element(element);
9058 if (!game.magic_wall_active)
9059 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
9060 Store[newx][newy] = Store[x][y];
9062 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
9064 Feld[x][y] = get_next_element(element);
9065 if (!game.magic_wall_active)
9066 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9067 element = Feld[newx][newy] = Store[x][y];
9069 #if USE_NEW_CUSTOM_VALUE
9070 InitField(newx, newy, FALSE);
9073 else if (element == EL_DC_MAGIC_WALL_FILLING)
9075 element = Feld[newx][newy] = get_next_element(element);
9076 if (!game.magic_wall_active)
9077 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
9078 Store[newx][newy] = Store[x][y];
9080 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
9082 Feld[x][y] = get_next_element(element);
9083 if (!game.magic_wall_active)
9084 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
9085 element = Feld[newx][newy] = Store[x][y];
9087 #if USE_NEW_CUSTOM_VALUE
9088 InitField(newx, newy, FALSE);
9091 else if (element == EL_AMOEBA_DROPPING)
9093 Feld[x][y] = get_next_element(element);
9094 element = Feld[newx][newy] = Store[x][y];
9096 else if (element == EL_SOKOBAN_OBJECT)
9099 Feld[x][y] = Back[x][y];
9101 if (Back[newx][newy])
9102 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
9104 Back[x][y] = Back[newx][newy] = 0;
9107 Store[x][y] = EL_EMPTY;
9112 MovDelay[newx][newy] = 0;
9114 if (CAN_CHANGE_OR_HAS_ACTION(element))
9116 /* copy element change control values to new field */
9117 ChangeDelay[newx][newy] = ChangeDelay[x][y];
9118 ChangePage[newx][newy] = ChangePage[x][y];
9119 ChangeCount[newx][newy] = ChangeCount[x][y];
9120 ChangeEvent[newx][newy] = ChangeEvent[x][y];
9123 #if USE_NEW_CUSTOM_VALUE
9124 CustomValue[newx][newy] = CustomValue[x][y];
9127 ChangeDelay[x][y] = 0;
9128 ChangePage[x][y] = -1;
9129 ChangeCount[x][y] = 0;
9130 ChangeEvent[x][y] = -1;
9132 #if USE_NEW_CUSTOM_VALUE
9133 CustomValue[x][y] = 0;
9136 /* copy animation control values to new field */
9137 GfxFrame[newx][newy] = GfxFrame[x][y];
9138 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
9139 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
9140 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
9142 Pushed[x][y] = Pushed[newx][newy] = FALSE;
9144 /* some elements can leave other elements behind after moving */
9146 if (ei->move_leave_element != EL_EMPTY &&
9147 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9148 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9150 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
9151 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9152 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9155 int move_leave_element = ei->move_leave_element;
9159 /* this makes it possible to leave the removed element again */
9160 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9161 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9163 /* this makes it possible to leave the removed element again */
9164 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9165 move_leave_element = stored;
9168 /* this makes it possible to leave the removed element again */
9169 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
9170 ei->move_leave_element == EL_TRIGGER_ELEMENT)
9171 move_leave_element = stored;
9174 Feld[x][y] = move_leave_element;
9176 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
9177 MovDir[x][y] = direction;
9179 InitField(x, y, FALSE);
9181 if (GFX_CRUMBLED(Feld[x][y]))
9182 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9184 if (ELEM_IS_PLAYER(move_leave_element))
9185 RelocatePlayer(x, y, move_leave_element);
9188 /* do this after checking for left-behind element */
9189 ResetGfxAnimation(x, y); /* reset animation values for old field */
9191 if (!CAN_MOVE(element) ||
9192 (CAN_FALL(element) && direction == MV_DOWN &&
9193 (element == EL_SPRING ||
9194 element_info[element].move_pattern == MV_WHEN_PUSHED ||
9195 element_info[element].move_pattern == MV_WHEN_DROPPED)))
9196 GfxDir[x][y] = MovDir[newx][newy] = 0;
9198 TEST_DrawLevelField(x, y);
9199 TEST_DrawLevelField(newx, newy);
9201 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
9203 /* prevent pushed element from moving on in pushed direction */
9204 if (pushed_by_player && CAN_MOVE(element) &&
9205 element_info[element].move_pattern & MV_ANY_DIRECTION &&
9206 !(element_info[element].move_pattern & direction))
9207 TurnRound(newx, newy);
9209 /* prevent elements on conveyor belt from moving on in last direction */
9210 if (pushed_by_conveyor && CAN_FALL(element) &&
9211 direction & MV_HORIZONTAL)
9212 MovDir[newx][newy] = 0;
9214 if (!pushed_by_player)
9216 int nextx = newx + dx, nexty = newy + dy;
9217 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9219 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9221 if (CAN_FALL(element) && direction == MV_DOWN)
9222 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9224 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9225 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9227 #if USE_FIX_IMPACT_COLLISION
9228 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9229 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9233 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
9235 TestIfBadThingTouchesPlayer(newx, newy);
9236 TestIfBadThingTouchesFriend(newx, newy);
9238 if (!IS_CUSTOM_ELEMENT(element))
9239 TestIfBadThingTouchesOtherBadThing(newx, newy);
9241 else if (element == EL_PENGUIN)
9242 TestIfFriendTouchesBadThing(newx, newy);
9244 if (DONT_GET_HIT_BY(element))
9246 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9249 /* give the player one last chance (one more frame) to move away */
9250 if (CAN_FALL(element) && direction == MV_DOWN &&
9251 (last_line || (!IS_FREE(x, newy + 1) &&
9252 (!IS_PLAYER(x, newy + 1) ||
9253 game.engine_version < VERSION_IDENT(3,1,1,0)))))
9256 if (pushed_by_player && !game.use_change_when_pushing_bug)
9258 int push_side = MV_DIR_OPPOSITE(direction);
9259 struct PlayerInfo *player = PLAYERINFO(x, y);
9261 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9262 player->index_bit, push_side);
9263 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9264 player->index_bit, push_side);
9267 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
9268 MovDelay[newx][newy] = 1;
9270 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9272 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
9275 if (ChangePage[newx][newy] != -1) /* delayed change */
9277 int page = ChangePage[newx][newy];
9278 struct ElementChangeInfo *change = &ei->change_page[page];
9280 ChangePage[newx][newy] = -1;
9282 if (change->can_change)
9284 if (ChangeElement(newx, newy, element, page))
9286 if (change->post_change_function)
9287 change->post_change_function(newx, newy);
9291 if (change->has_action)
9292 ExecuteCustomElementAction(newx, newy, element, page);
9296 TestIfElementHitsCustomElement(newx, newy, direction);
9297 TestIfPlayerTouchesCustomElement(newx, newy);
9298 TestIfElementTouchesCustomElement(newx, newy);
9300 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9301 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9302 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9303 MV_DIR_OPPOSITE(direction));
9306 int AmoebeNachbarNr(int ax, int ay)
9309 int element = Feld[ax][ay];
9311 static int xy[4][2] =
9319 for (i = 0; i < NUM_DIRECTIONS; i++)
9321 int x = ax + xy[i][0];
9322 int y = ay + xy[i][1];
9324 if (!IN_LEV_FIELD(x, y))
9327 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9328 group_nr = AmoebaNr[x][y];
9334 void AmoebenVereinigen(int ax, int ay)
9336 int i, x, y, xx, yy;
9337 int new_group_nr = AmoebaNr[ax][ay];
9338 static int xy[4][2] =
9346 if (new_group_nr == 0)
9349 for (i = 0; i < NUM_DIRECTIONS; i++)
9354 if (!IN_LEV_FIELD(x, y))
9357 if ((Feld[x][y] == EL_AMOEBA_FULL ||
9358 Feld[x][y] == EL_BD_AMOEBA ||
9359 Feld[x][y] == EL_AMOEBA_DEAD) &&
9360 AmoebaNr[x][y] != new_group_nr)
9362 int old_group_nr = AmoebaNr[x][y];
9364 if (old_group_nr == 0)
9367 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9368 AmoebaCnt[old_group_nr] = 0;
9369 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9370 AmoebaCnt2[old_group_nr] = 0;
9372 SCAN_PLAYFIELD(xx, yy)
9374 if (AmoebaNr[xx][yy] == old_group_nr)
9375 AmoebaNr[xx][yy] = new_group_nr;
9381 void AmoebeUmwandeln(int ax, int ay)
9385 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9387 int group_nr = AmoebaNr[ax][ay];
9392 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9393 printf("AmoebeUmwandeln(): This should never happen!\n");
9398 SCAN_PLAYFIELD(x, y)
9400 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9403 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9407 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9408 SND_AMOEBA_TURNING_TO_GEM :
9409 SND_AMOEBA_TURNING_TO_ROCK));
9414 static int xy[4][2] =
9422 for (i = 0; i < NUM_DIRECTIONS; i++)
9427 if (!IN_LEV_FIELD(x, y))
9430 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9432 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9433 SND_AMOEBA_TURNING_TO_GEM :
9434 SND_AMOEBA_TURNING_TO_ROCK));
9441 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9444 int group_nr = AmoebaNr[ax][ay];
9445 boolean done = FALSE;
9450 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9451 printf("AmoebeUmwandelnBD(): This should never happen!\n");
9456 SCAN_PLAYFIELD(x, y)
9458 if (AmoebaNr[x][y] == group_nr &&
9459 (Feld[x][y] == EL_AMOEBA_DEAD ||
9460 Feld[x][y] == EL_BD_AMOEBA ||
9461 Feld[x][y] == EL_AMOEBA_GROWING))
9464 Feld[x][y] = new_element;
9465 InitField(x, y, FALSE);
9466 TEST_DrawLevelField(x, y);
9472 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9473 SND_BD_AMOEBA_TURNING_TO_ROCK :
9474 SND_BD_AMOEBA_TURNING_TO_GEM));
9477 void AmoebeWaechst(int x, int y)
9479 static unsigned long sound_delay = 0;
9480 static unsigned long sound_delay_value = 0;
9482 if (!MovDelay[x][y]) /* start new growing cycle */
9486 if (DelayReached(&sound_delay, sound_delay_value))
9488 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9489 sound_delay_value = 30;
9493 if (MovDelay[x][y]) /* wait some time before growing bigger */
9496 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9498 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9499 6 - MovDelay[x][y]);
9501 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9504 if (!MovDelay[x][y])
9506 Feld[x][y] = Store[x][y];
9508 TEST_DrawLevelField(x, y);
9513 void AmoebaDisappearing(int x, int y)
9515 static unsigned long sound_delay = 0;
9516 static unsigned long sound_delay_value = 0;
9518 if (!MovDelay[x][y]) /* start new shrinking cycle */
9522 if (DelayReached(&sound_delay, sound_delay_value))
9523 sound_delay_value = 30;
9526 if (MovDelay[x][y]) /* wait some time before shrinking */
9529 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9531 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9532 6 - MovDelay[x][y]);
9534 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9537 if (!MovDelay[x][y])
9539 Feld[x][y] = EL_EMPTY;
9540 TEST_DrawLevelField(x, y);
9542 /* don't let mole enter this field in this cycle;
9543 (give priority to objects falling to this field from above) */
9549 void AmoebeAbleger(int ax, int ay)
9552 int element = Feld[ax][ay];
9553 int graphic = el2img(element);
9554 int newax = ax, neway = ay;
9555 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9556 static int xy[4][2] =
9564 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9566 Feld[ax][ay] = EL_AMOEBA_DEAD;
9567 TEST_DrawLevelField(ax, ay);
9571 if (IS_ANIMATED(graphic))
9572 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9574 if (!MovDelay[ax][ay]) /* start making new amoeba field */
9575 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9577 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
9580 if (MovDelay[ax][ay])
9584 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9587 int x = ax + xy[start][0];
9588 int y = ay + xy[start][1];
9590 if (!IN_LEV_FIELD(x, y))
9593 if (IS_FREE(x, y) ||
9594 CAN_GROW_INTO(Feld[x][y]) ||
9595 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9596 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9602 if (newax == ax && neway == ay)
9605 else /* normal or "filled" (BD style) amoeba */
9608 boolean waiting_for_player = FALSE;
9610 for (i = 0; i < NUM_DIRECTIONS; i++)
9612 int j = (start + i) % 4;
9613 int x = ax + xy[j][0];
9614 int y = ay + xy[j][1];
9616 if (!IN_LEV_FIELD(x, y))
9619 if (IS_FREE(x, y) ||
9620 CAN_GROW_INTO(Feld[x][y]) ||
9621 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9622 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9628 else if (IS_PLAYER(x, y))
9629 waiting_for_player = TRUE;
9632 if (newax == ax && neway == ay) /* amoeba cannot grow */
9634 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9636 Feld[ax][ay] = EL_AMOEBA_DEAD;
9637 TEST_DrawLevelField(ax, ay);
9638 AmoebaCnt[AmoebaNr[ax][ay]]--;
9640 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
9642 if (element == EL_AMOEBA_FULL)
9643 AmoebeUmwandeln(ax, ay);
9644 else if (element == EL_BD_AMOEBA)
9645 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9650 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9652 /* amoeba gets larger by growing in some direction */
9654 int new_group_nr = AmoebaNr[ax][ay];
9657 if (new_group_nr == 0)
9659 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9660 printf("AmoebeAbleger(): This should never happen!\n");
9665 AmoebaNr[newax][neway] = new_group_nr;
9666 AmoebaCnt[new_group_nr]++;
9667 AmoebaCnt2[new_group_nr]++;
9669 /* if amoeba touches other amoeba(s) after growing, unify them */
9670 AmoebenVereinigen(newax, neway);
9672 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9674 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9680 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9681 (neway == lev_fieldy - 1 && newax != ax))
9683 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
9684 Store[newax][neway] = element;
9686 else if (neway == ay || element == EL_EMC_DRIPPER)
9688 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
9690 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9694 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
9695 Feld[ax][ay] = EL_AMOEBA_DROPPING;
9696 Store[ax][ay] = EL_AMOEBA_DROP;
9697 ContinueMoving(ax, ay);
9701 TEST_DrawLevelField(newax, neway);
9704 void Life(int ax, int ay)
9708 int element = Feld[ax][ay];
9709 int graphic = el2img(element);
9710 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9712 boolean changed = FALSE;
9714 if (IS_ANIMATED(graphic))
9715 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9720 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
9721 MovDelay[ax][ay] = life_time;
9723 if (MovDelay[ax][ay]) /* wait some time before next cycle */
9726 if (MovDelay[ax][ay])
9730 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9732 int xx = ax+x1, yy = ay+y1;
9735 if (!IN_LEV_FIELD(xx, yy))
9738 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9740 int x = xx+x2, y = yy+y2;
9742 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9745 if (((Feld[x][y] == element ||
9746 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9748 (IS_FREE(x, y) && Stop[x][y]))
9752 if (xx == ax && yy == ay) /* field in the middle */
9754 if (nachbarn < life_parameter[0] ||
9755 nachbarn > life_parameter[1])
9757 Feld[xx][yy] = EL_EMPTY;
9759 TEST_DrawLevelField(xx, yy);
9760 Stop[xx][yy] = TRUE;
9764 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9765 { /* free border field */
9766 if (nachbarn >= life_parameter[2] &&
9767 nachbarn <= life_parameter[3])
9769 Feld[xx][yy] = element;
9770 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9772 TEST_DrawLevelField(xx, yy);
9773 Stop[xx][yy] = TRUE;
9780 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9781 SND_GAME_OF_LIFE_GROWING);
9784 static void InitRobotWheel(int x, int y)
9786 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9789 static void RunRobotWheel(int x, int y)
9791 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9794 static void StopRobotWheel(int x, int y)
9796 if (ZX == x && ZY == y)
9800 game.robot_wheel_active = FALSE;
9804 static void InitTimegateWheel(int x, int y)
9806 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9809 static void RunTimegateWheel(int x, int y)
9811 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9814 static void InitMagicBallDelay(int x, int y)
9817 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9819 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9823 static void ActivateMagicBall(int bx, int by)
9827 if (level.ball_random)
9829 int pos_border = RND(8); /* select one of the eight border elements */
9830 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9831 int xx = pos_content % 3;
9832 int yy = pos_content / 3;
9837 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9838 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9842 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9844 int xx = x - bx + 1;
9845 int yy = y - by + 1;
9847 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9848 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9852 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9855 void CheckExit(int x, int y)
9857 if (local_player->gems_still_needed > 0 ||
9858 local_player->sokobanfields_still_needed > 0 ||
9859 local_player->lights_still_needed > 0)
9861 int element = Feld[x][y];
9862 int graphic = el2img(element);
9864 if (IS_ANIMATED(graphic))
9865 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9870 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9873 Feld[x][y] = EL_EXIT_OPENING;
9875 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9878 void CheckExitEM(int x, int y)
9880 if (local_player->gems_still_needed > 0 ||
9881 local_player->sokobanfields_still_needed > 0 ||
9882 local_player->lights_still_needed > 0)
9884 int element = Feld[x][y];
9885 int graphic = el2img(element);
9887 if (IS_ANIMATED(graphic))
9888 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9893 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9896 Feld[x][y] = EL_EM_EXIT_OPENING;
9898 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9901 void CheckExitSteel(int x, int y)
9903 if (local_player->gems_still_needed > 0 ||
9904 local_player->sokobanfields_still_needed > 0 ||
9905 local_player->lights_still_needed > 0)
9907 int element = Feld[x][y];
9908 int graphic = el2img(element);
9910 if (IS_ANIMATED(graphic))
9911 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9916 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9919 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9921 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9924 void CheckExitSteelEM(int x, int y)
9926 if (local_player->gems_still_needed > 0 ||
9927 local_player->sokobanfields_still_needed > 0 ||
9928 local_player->lights_still_needed > 0)
9930 int element = Feld[x][y];
9931 int graphic = el2img(element);
9933 if (IS_ANIMATED(graphic))
9934 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9939 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9942 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9944 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9947 void CheckExitSP(int x, int y)
9949 if (local_player->gems_still_needed > 0)
9951 int element = Feld[x][y];
9952 int graphic = el2img(element);
9954 if (IS_ANIMATED(graphic))
9955 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9960 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9963 Feld[x][y] = EL_SP_EXIT_OPENING;
9965 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9968 static void CloseAllOpenTimegates()
9972 SCAN_PLAYFIELD(x, y)
9974 int element = Feld[x][y];
9976 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9978 Feld[x][y] = EL_TIMEGATE_CLOSING;
9980 PlayLevelSoundAction(x, y, ACTION_CLOSING);
9985 void DrawTwinkleOnField(int x, int y)
9987 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9990 if (Feld[x][y] == EL_BD_DIAMOND)
9993 if (MovDelay[x][y] == 0) /* next animation frame */
9994 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9996 if (MovDelay[x][y] != 0) /* wait some time before next frame */
10000 DrawLevelElementAnimation(x, y, Feld[x][y]);
10002 if (MovDelay[x][y] != 0)
10004 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
10005 10 - MovDelay[x][y]);
10007 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
10012 void MauerWaechst(int x, int y)
10016 if (!MovDelay[x][y]) /* next animation frame */
10017 MovDelay[x][y] = 3 * delay;
10019 if (MovDelay[x][y]) /* wait some time before next frame */
10023 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
10025 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
10026 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
10028 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
10031 if (!MovDelay[x][y])
10033 if (MovDir[x][y] == MV_LEFT)
10035 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
10036 TEST_DrawLevelField(x - 1, y);
10038 else if (MovDir[x][y] == MV_RIGHT)
10040 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
10041 TEST_DrawLevelField(x + 1, y);
10043 else if (MovDir[x][y] == MV_UP)
10045 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
10046 TEST_DrawLevelField(x, y - 1);
10050 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
10051 TEST_DrawLevelField(x, y + 1);
10054 Feld[x][y] = Store[x][y];
10056 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
10057 TEST_DrawLevelField(x, y);
10062 void MauerAbleger(int ax, int ay)
10064 int element = Feld[ax][ay];
10065 int graphic = el2img(element);
10066 boolean oben_frei = FALSE, unten_frei = FALSE;
10067 boolean links_frei = FALSE, rechts_frei = FALSE;
10068 boolean oben_massiv = FALSE, unten_massiv = FALSE;
10069 boolean links_massiv = FALSE, rechts_massiv = FALSE;
10070 boolean new_wall = FALSE;
10072 if (IS_ANIMATED(graphic))
10073 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10075 if (!MovDelay[ax][ay]) /* start building new wall */
10076 MovDelay[ax][ay] = 6;
10078 if (MovDelay[ax][ay]) /* wait some time before building new wall */
10080 MovDelay[ax][ay]--;
10081 if (MovDelay[ax][ay])
10085 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10087 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10089 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10091 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10092 rechts_frei = TRUE;
10094 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
10095 element == EL_EXPANDABLE_WALL_ANY)
10099 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
10100 Store[ax][ay-1] = element;
10101 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10102 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10103 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10104 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
10109 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
10110 Store[ax][ay+1] = element;
10111 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10112 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10113 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10114 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
10119 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10120 element == EL_EXPANDABLE_WALL_ANY ||
10121 element == EL_EXPANDABLE_WALL ||
10122 element == EL_BD_EXPANDABLE_WALL)
10126 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
10127 Store[ax-1][ay] = element;
10128 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10129 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10130 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10131 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
10137 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
10138 Store[ax+1][ay] = element;
10139 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10140 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10141 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10142 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
10147 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
10148 TEST_DrawLevelField(ax, ay);
10150 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10151 oben_massiv = TRUE;
10152 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10153 unten_massiv = TRUE;
10154 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10155 links_massiv = TRUE;
10156 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10157 rechts_massiv = TRUE;
10159 if (((oben_massiv && unten_massiv) ||
10160 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10161 element == EL_EXPANDABLE_WALL) &&
10162 ((links_massiv && rechts_massiv) ||
10163 element == EL_EXPANDABLE_WALL_VERTICAL))
10164 Feld[ax][ay] = EL_WALL;
10167 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10170 void MauerAblegerStahl(int ax, int ay)
10172 int element = Feld[ax][ay];
10173 int graphic = el2img(element);
10174 boolean oben_frei = FALSE, unten_frei = FALSE;
10175 boolean links_frei = FALSE, rechts_frei = FALSE;
10176 boolean oben_massiv = FALSE, unten_massiv = FALSE;
10177 boolean links_massiv = FALSE, rechts_massiv = FALSE;
10178 boolean new_wall = FALSE;
10180 if (IS_ANIMATED(graphic))
10181 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10183 if (!MovDelay[ax][ay]) /* start building new wall */
10184 MovDelay[ax][ay] = 6;
10186 if (MovDelay[ax][ay]) /* wait some time before building new wall */
10188 MovDelay[ax][ay]--;
10189 if (MovDelay[ax][ay])
10193 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10195 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10197 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10199 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10200 rechts_frei = TRUE;
10202 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10203 element == EL_EXPANDABLE_STEELWALL_ANY)
10207 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10208 Store[ax][ay-1] = element;
10209 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10210 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10211 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10212 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10217 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10218 Store[ax][ay+1] = element;
10219 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10220 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10221 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10222 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10227 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10228 element == EL_EXPANDABLE_STEELWALL_ANY)
10232 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10233 Store[ax-1][ay] = element;
10234 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10235 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10236 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10237 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10243 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10244 Store[ax+1][ay] = element;
10245 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10246 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10247 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10248 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10253 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10254 oben_massiv = TRUE;
10255 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10256 unten_massiv = TRUE;
10257 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10258 links_massiv = TRUE;
10259 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10260 rechts_massiv = TRUE;
10262 if (((oben_massiv && unten_massiv) ||
10263 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10264 ((links_massiv && rechts_massiv) ||
10265 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10266 Feld[ax][ay] = EL_STEELWALL;
10269 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10272 void CheckForDragon(int x, int y)
10275 boolean dragon_found = FALSE;
10276 static int xy[4][2] =
10284 for (i = 0; i < NUM_DIRECTIONS; i++)
10286 for (j = 0; j < 4; j++)
10288 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10290 if (IN_LEV_FIELD(xx, yy) &&
10291 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10293 if (Feld[xx][yy] == EL_DRAGON)
10294 dragon_found = TRUE;
10303 for (i = 0; i < NUM_DIRECTIONS; i++)
10305 for (j = 0; j < 3; j++)
10307 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10309 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10311 Feld[xx][yy] = EL_EMPTY;
10312 TEST_DrawLevelField(xx, yy);
10321 static void InitBuggyBase(int x, int y)
10323 int element = Feld[x][y];
10324 int activating_delay = FRAMES_PER_SECOND / 4;
10326 ChangeDelay[x][y] =
10327 (element == EL_SP_BUGGY_BASE ?
10328 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10329 element == EL_SP_BUGGY_BASE_ACTIVATING ?
10331 element == EL_SP_BUGGY_BASE_ACTIVE ?
10332 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10335 static void WarnBuggyBase(int x, int y)
10338 static int xy[4][2] =
10346 for (i = 0; i < NUM_DIRECTIONS; i++)
10348 int xx = x + xy[i][0];
10349 int yy = y + xy[i][1];
10351 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10353 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10360 static void InitTrap(int x, int y)
10362 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10365 static void ActivateTrap(int x, int y)
10367 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10370 static void ChangeActiveTrap(int x, int y)
10372 int graphic = IMG_TRAP_ACTIVE;
10374 /* if new animation frame was drawn, correct crumbled sand border */
10375 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10376 TEST_DrawLevelFieldCrumbled(x, y);
10379 static int getSpecialActionElement(int element, int number, int base_element)
10381 return (element != EL_EMPTY ? element :
10382 number != -1 ? base_element + number - 1 :
10386 static int getModifiedActionNumber(int value_old, int operator, int operand,
10387 int value_min, int value_max)
10389 int value_new = (operator == CA_MODE_SET ? operand :
10390 operator == CA_MODE_ADD ? value_old + operand :
10391 operator == CA_MODE_SUBTRACT ? value_old - operand :
10392 operator == CA_MODE_MULTIPLY ? value_old * operand :
10393 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
10394 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
10397 return (value_new < value_min ? value_min :
10398 value_new > value_max ? value_max :
10402 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10404 struct ElementInfo *ei = &element_info[element];
10405 struct ElementChangeInfo *change = &ei->change_page[page];
10406 int target_element = change->target_element;
10407 int action_type = change->action_type;
10408 int action_mode = change->action_mode;
10409 int action_arg = change->action_arg;
10410 int action_element = change->action_element;
10413 if (!change->has_action)
10416 /* ---------- determine action paramater values -------------------------- */
10418 int level_time_value =
10419 (level.time > 0 ? TimeLeft :
10422 int action_arg_element_raw =
10423 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
10424 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10425 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
10426 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
10427 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10428 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
10429 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
10431 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10434 if (action_arg_element_raw == EL_GROUP_START)
10435 printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10438 int action_arg_direction =
10439 (action_arg >= CA_ARG_DIRECTION_LEFT &&
10440 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10441 action_arg == CA_ARG_DIRECTION_TRIGGER ?
10442 change->actual_trigger_side :
10443 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10444 MV_DIR_OPPOSITE(change->actual_trigger_side) :
10447 int action_arg_number_min =
10448 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10451 int action_arg_number_max =
10452 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10453 action_type == CA_SET_LEVEL_GEMS ? 999 :
10454 action_type == CA_SET_LEVEL_TIME ? 9999 :
10455 action_type == CA_SET_LEVEL_SCORE ? 99999 :
10456 action_type == CA_SET_CE_VALUE ? 9999 :
10457 action_type == CA_SET_CE_SCORE ? 9999 :
10460 int action_arg_number_reset =
10461 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10462 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10463 action_type == CA_SET_LEVEL_TIME ? level.time :
10464 action_type == CA_SET_LEVEL_SCORE ? 0 :
10465 #if USE_NEW_CUSTOM_VALUE
10466 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10468 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10470 action_type == CA_SET_CE_SCORE ? 0 :
10473 int action_arg_number =
10474 (action_arg <= CA_ARG_MAX ? action_arg :
10475 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10476 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10477 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10478 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10479 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10480 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10481 #if USE_NEW_CUSTOM_VALUE
10482 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10484 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10486 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10487 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10488 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10489 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10490 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10491 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10492 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10493 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10494 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10495 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10496 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10497 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
10498 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10499 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
10502 int action_arg_number_old =
10503 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10504 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10505 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10506 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10507 action_type == CA_SET_CE_SCORE ? ei->collect_score :
10510 int action_arg_number_new =
10511 getModifiedActionNumber(action_arg_number_old,
10512 action_mode, action_arg_number,
10513 action_arg_number_min, action_arg_number_max);
10516 int trigger_player_bits =
10517 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10518 change->actual_trigger_player_bits : change->trigger_player);
10520 int trigger_player_bits =
10521 (change->actual_trigger_player >= EL_PLAYER_1 &&
10522 change->actual_trigger_player <= EL_PLAYER_4 ?
10523 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10527 int action_arg_player_bits =
10528 (action_arg >= CA_ARG_PLAYER_1 &&
10529 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10530 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10531 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10534 /* ---------- execute action -------------------------------------------- */
10536 switch (action_type)
10543 /* ---------- level actions ------------------------------------------- */
10545 case CA_RESTART_LEVEL:
10547 game.restart_level = TRUE;
10552 case CA_SHOW_ENVELOPE:
10554 int element = getSpecialActionElement(action_arg_element,
10555 action_arg_number, EL_ENVELOPE_1);
10557 if (IS_ENVELOPE(element))
10558 local_player->show_envelope = element;
10563 case CA_SET_LEVEL_TIME:
10565 if (level.time > 0) /* only modify limited time value */
10567 TimeLeft = action_arg_number_new;
10570 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10572 DisplayGameControlValues();
10574 DrawGameValue_Time(TimeLeft);
10577 if (!TimeLeft && setup.time_limit)
10578 for (i = 0; i < MAX_PLAYERS; i++)
10579 KillPlayer(&stored_player[i]);
10585 case CA_SET_LEVEL_SCORE:
10587 local_player->score = action_arg_number_new;
10590 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10592 DisplayGameControlValues();
10594 DrawGameValue_Score(local_player->score);
10600 case CA_SET_LEVEL_GEMS:
10602 local_player->gems_still_needed = action_arg_number_new;
10605 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10607 DisplayGameControlValues();
10609 DrawGameValue_Emeralds(local_player->gems_still_needed);
10615 #if !USE_PLAYER_GRAVITY
10616 case CA_SET_LEVEL_GRAVITY:
10618 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10619 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10620 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10626 case CA_SET_LEVEL_WIND:
10628 game.wind_direction = action_arg_direction;
10633 case CA_SET_LEVEL_RANDOM_SEED:
10636 /* ensure that setting a new random seed while playing is predictable */
10637 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10639 InitRND(action_arg_number_new);
10643 printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10651 for (i = 0; i < 9; i++)
10652 printf("%d, ", RND(2));
10660 /* ---------- player actions ------------------------------------------ */
10662 case CA_MOVE_PLAYER:
10664 /* automatically move to the next field in specified direction */
10665 for (i = 0; i < MAX_PLAYERS; i++)
10666 if (trigger_player_bits & (1 << i))
10667 stored_player[i].programmed_action = action_arg_direction;
10672 case CA_EXIT_PLAYER:
10674 for (i = 0; i < MAX_PLAYERS; i++)
10675 if (action_arg_player_bits & (1 << i))
10676 PlayerWins(&stored_player[i]);
10681 case CA_KILL_PLAYER:
10683 for (i = 0; i < MAX_PLAYERS; i++)
10684 if (action_arg_player_bits & (1 << i))
10685 KillPlayer(&stored_player[i]);
10690 case CA_SET_PLAYER_KEYS:
10692 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10693 int element = getSpecialActionElement(action_arg_element,
10694 action_arg_number, EL_KEY_1);
10696 if (IS_KEY(element))
10698 for (i = 0; i < MAX_PLAYERS; i++)
10700 if (trigger_player_bits & (1 << i))
10702 stored_player[i].key[KEY_NR(element)] = key_state;
10704 DrawGameDoorValues();
10712 case CA_SET_PLAYER_SPEED:
10715 printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10718 for (i = 0; i < MAX_PLAYERS; i++)
10720 if (trigger_player_bits & (1 << i))
10722 int move_stepsize = TILEX / stored_player[i].move_delay_value;
10724 if (action_arg == CA_ARG_SPEED_FASTER &&
10725 stored_player[i].cannot_move)
10727 action_arg_number = STEPSIZE_VERY_SLOW;
10729 else if (action_arg == CA_ARG_SPEED_SLOWER ||
10730 action_arg == CA_ARG_SPEED_FASTER)
10732 action_arg_number = 2;
10733 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10736 else if (action_arg == CA_ARG_NUMBER_RESET)
10738 action_arg_number = level.initial_player_stepsize[i];
10742 getModifiedActionNumber(move_stepsize,
10745 action_arg_number_min,
10746 action_arg_number_max);
10748 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10755 case CA_SET_PLAYER_SHIELD:
10757 for (i = 0; i < MAX_PLAYERS; i++)
10759 if (trigger_player_bits & (1 << i))
10761 if (action_arg == CA_ARG_SHIELD_OFF)
10763 stored_player[i].shield_normal_time_left = 0;
10764 stored_player[i].shield_deadly_time_left = 0;
10766 else if (action_arg == CA_ARG_SHIELD_NORMAL)
10768 stored_player[i].shield_normal_time_left = 999999;
10770 else if (action_arg == CA_ARG_SHIELD_DEADLY)
10772 stored_player[i].shield_normal_time_left = 999999;
10773 stored_player[i].shield_deadly_time_left = 999999;
10781 #if USE_PLAYER_GRAVITY
10782 case CA_SET_PLAYER_GRAVITY:
10784 for (i = 0; i < MAX_PLAYERS; i++)
10786 if (trigger_player_bits & (1 << i))
10788 stored_player[i].gravity =
10789 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10790 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10791 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10792 stored_player[i].gravity);
10800 case CA_SET_PLAYER_ARTWORK:
10802 for (i = 0; i < MAX_PLAYERS; i++)
10804 if (trigger_player_bits & (1 << i))
10806 int artwork_element = action_arg_element;
10808 if (action_arg == CA_ARG_ELEMENT_RESET)
10810 (level.use_artwork_element[i] ? level.artwork_element[i] :
10811 stored_player[i].element_nr);
10813 #if USE_GFX_RESET_PLAYER_ARTWORK
10814 if (stored_player[i].artwork_element != artwork_element)
10815 stored_player[i].Frame = 0;
10818 stored_player[i].artwork_element = artwork_element;
10820 SetPlayerWaiting(&stored_player[i], FALSE);
10822 /* set number of special actions for bored and sleeping animation */
10823 stored_player[i].num_special_action_bored =
10824 get_num_special_action(artwork_element,
10825 ACTION_BORING_1, ACTION_BORING_LAST);
10826 stored_player[i].num_special_action_sleeping =
10827 get_num_special_action(artwork_element,
10828 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10835 case CA_SET_PLAYER_INVENTORY:
10837 for (i = 0; i < MAX_PLAYERS; i++)
10839 struct PlayerInfo *player = &stored_player[i];
10842 if (trigger_player_bits & (1 << i))
10844 int inventory_element = action_arg_element;
10846 if (action_arg == CA_ARG_ELEMENT_TARGET ||
10847 action_arg == CA_ARG_ELEMENT_TRIGGER ||
10848 action_arg == CA_ARG_ELEMENT_ACTION)
10850 int element = inventory_element;
10851 int collect_count = element_info[element].collect_count_initial;
10853 if (!IS_CUSTOM_ELEMENT(element))
10856 if (collect_count == 0)
10857 player->inventory_infinite_element = element;
10859 for (k = 0; k < collect_count; k++)
10860 if (player->inventory_size < MAX_INVENTORY_SIZE)
10861 player->inventory_element[player->inventory_size++] =
10864 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10865 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10866 action_arg == CA_ARG_INVENTORY_RM_ACTION)
10868 if (player->inventory_infinite_element != EL_UNDEFINED &&
10869 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10870 action_arg_element_raw))
10871 player->inventory_infinite_element = EL_UNDEFINED;
10873 for (k = 0, j = 0; j < player->inventory_size; j++)
10875 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10876 action_arg_element_raw))
10877 player->inventory_element[k++] = player->inventory_element[j];
10880 player->inventory_size = k;
10882 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10884 if (player->inventory_size > 0)
10886 for (j = 0; j < player->inventory_size - 1; j++)
10887 player->inventory_element[j] = player->inventory_element[j + 1];
10889 player->inventory_size--;
10892 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10894 if (player->inventory_size > 0)
10895 player->inventory_size--;
10897 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10899 player->inventory_infinite_element = EL_UNDEFINED;
10900 player->inventory_size = 0;
10902 else if (action_arg == CA_ARG_INVENTORY_RESET)
10904 player->inventory_infinite_element = EL_UNDEFINED;
10905 player->inventory_size = 0;
10907 if (level.use_initial_inventory[i])
10909 for (j = 0; j < level.initial_inventory_size[i]; j++)
10911 int element = level.initial_inventory_content[i][j];
10912 int collect_count = element_info[element].collect_count_initial;
10914 if (!IS_CUSTOM_ELEMENT(element))
10917 if (collect_count == 0)
10918 player->inventory_infinite_element = element;
10920 for (k = 0; k < collect_count; k++)
10921 if (player->inventory_size < MAX_INVENTORY_SIZE)
10922 player->inventory_element[player->inventory_size++] =
10933 /* ---------- CE actions ---------------------------------------------- */
10935 case CA_SET_CE_VALUE:
10937 #if USE_NEW_CUSTOM_VALUE
10938 int last_ce_value = CustomValue[x][y];
10940 CustomValue[x][y] = action_arg_number_new;
10942 if (CustomValue[x][y] != last_ce_value)
10944 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10945 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10947 if (CustomValue[x][y] == 0)
10949 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10950 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10958 case CA_SET_CE_SCORE:
10960 #if USE_NEW_CUSTOM_VALUE
10961 int last_ce_score = ei->collect_score;
10963 ei->collect_score = action_arg_number_new;
10965 if (ei->collect_score != last_ce_score)
10967 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10968 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10970 if (ei->collect_score == 0)
10974 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10975 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10978 This is a very special case that seems to be a mixture between
10979 CheckElementChange() and CheckTriggeredElementChange(): while
10980 the first one only affects single elements that are triggered
10981 directly, the second one affects multiple elements in the playfield
10982 that are triggered indirectly by another element. This is a third
10983 case: Changing the CE score always affects multiple identical CEs,
10984 so every affected CE must be checked, not only the single CE for
10985 which the CE score was changed in the first place (as every instance
10986 of that CE shares the same CE score, and therefore also can change)!
10988 SCAN_PLAYFIELD(xx, yy)
10990 if (Feld[xx][yy] == element)
10991 CheckElementChange(xx, yy, element, EL_UNDEFINED,
10992 CE_SCORE_GETS_ZERO);
11001 case CA_SET_CE_ARTWORK:
11003 int artwork_element = action_arg_element;
11004 boolean reset_frame = FALSE;
11007 if (action_arg == CA_ARG_ELEMENT_RESET)
11008 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
11011 if (ei->gfx_element != artwork_element)
11012 reset_frame = TRUE;
11014 ei->gfx_element = artwork_element;
11016 SCAN_PLAYFIELD(xx, yy)
11018 if (Feld[xx][yy] == element)
11022 ResetGfxAnimation(xx, yy);
11023 ResetRandomAnimationValue(xx, yy);
11026 TEST_DrawLevelField(xx, yy);
11033 /* ---------- engine actions ------------------------------------------ */
11035 case CA_SET_ENGINE_SCAN_MODE:
11037 InitPlayfieldScanMode(action_arg);
11047 static void CreateFieldExt(int x, int y, int element, boolean is_change)
11049 int old_element = Feld[x][y];
11050 int new_element = GetElementFromGroupElement(element);
11051 int previous_move_direction = MovDir[x][y];
11052 #if USE_NEW_CUSTOM_VALUE
11053 int last_ce_value = CustomValue[x][y];
11055 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
11056 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
11057 boolean add_player_onto_element = (new_element_is_player &&
11058 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
11059 /* this breaks SnakeBite when a snake is
11060 halfway through a door that closes */
11061 /* NOW FIXED AT LEVEL INIT IN files.c */
11062 new_element != EL_SOKOBAN_FIELD_PLAYER &&
11064 IS_WALKABLE(old_element));
11067 /* check if element under the player changes from accessible to unaccessible
11068 (needed for special case of dropping element which then changes) */
11069 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11070 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11078 if (!add_player_onto_element)
11080 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
11081 RemoveMovingField(x, y);
11085 Feld[x][y] = new_element;
11087 #if !USE_GFX_RESET_GFX_ANIMATION
11088 ResetGfxAnimation(x, y);
11089 ResetRandomAnimationValue(x, y);
11092 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
11093 MovDir[x][y] = previous_move_direction;
11095 #if USE_NEW_CUSTOM_VALUE
11096 if (element_info[new_element].use_last_ce_value)
11097 CustomValue[x][y] = last_ce_value;
11100 InitField_WithBug1(x, y, FALSE);
11102 new_element = Feld[x][y]; /* element may have changed */
11104 #if USE_GFX_RESET_GFX_ANIMATION
11105 ResetGfxAnimation(x, y);
11106 ResetRandomAnimationValue(x, y);
11109 TEST_DrawLevelField(x, y);
11111 if (GFX_CRUMBLED(new_element))
11112 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
11116 /* check if element under the player changes from accessible to unaccessible
11117 (needed for special case of dropping element which then changes) */
11118 /* (must be checked after creating new element for walkable group elements) */
11119 #if USE_FIX_KILLED_BY_NON_WALKABLE
11120 if (IS_PLAYER(x, y) && !player_explosion_protected &&
11121 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11128 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11129 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11138 /* "ChangeCount" not set yet to allow "entered by player" change one time */
11139 if (new_element_is_player)
11140 RelocatePlayer(x, y, new_element);
11143 ChangeCount[x][y]++; /* count number of changes in the same frame */
11145 TestIfBadThingTouchesPlayer(x, y);
11146 TestIfPlayerTouchesCustomElement(x, y);
11147 TestIfElementTouchesCustomElement(x, y);
11150 static void CreateField(int x, int y, int element)
11152 CreateFieldExt(x, y, element, FALSE);
11155 static void CreateElementFromChange(int x, int y, int element)
11157 element = GET_VALID_RUNTIME_ELEMENT(element);
11159 #if USE_STOP_CHANGED_ELEMENTS
11160 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11162 int old_element = Feld[x][y];
11164 /* prevent changed element from moving in same engine frame
11165 unless both old and new element can either fall or move */
11166 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
11167 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
11172 CreateFieldExt(x, y, element, TRUE);
11175 static boolean ChangeElement(int x, int y, int element, int page)
11177 struct ElementInfo *ei = &element_info[element];
11178 struct ElementChangeInfo *change = &ei->change_page[page];
11179 int ce_value = CustomValue[x][y];
11180 int ce_score = ei->collect_score;
11181 int target_element;
11182 int old_element = Feld[x][y];
11184 /* always use default change event to prevent running into a loop */
11185 if (ChangeEvent[x][y] == -1)
11186 ChangeEvent[x][y] = CE_DELAY;
11188 if (ChangeEvent[x][y] == CE_DELAY)
11190 /* reset actual trigger element, trigger player and action element */
11191 change->actual_trigger_element = EL_EMPTY;
11192 change->actual_trigger_player = EL_EMPTY;
11193 change->actual_trigger_player_bits = CH_PLAYER_NONE;
11194 change->actual_trigger_side = CH_SIDE_NONE;
11195 change->actual_trigger_ce_value = 0;
11196 change->actual_trigger_ce_score = 0;
11199 /* do not change elements more than a specified maximum number of changes */
11200 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11203 ChangeCount[x][y]++; /* count number of changes in the same frame */
11205 if (change->explode)
11212 if (change->use_target_content)
11214 boolean complete_replace = TRUE;
11215 boolean can_replace[3][3];
11218 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11221 boolean is_walkable;
11222 boolean is_diggable;
11223 boolean is_collectible;
11224 boolean is_removable;
11225 boolean is_destructible;
11226 int ex = x + xx - 1;
11227 int ey = y + yy - 1;
11228 int content_element = change->target_content.e[xx][yy];
11231 can_replace[xx][yy] = TRUE;
11233 if (ex == x && ey == y) /* do not check changing element itself */
11236 if (content_element == EL_EMPTY_SPACE)
11238 can_replace[xx][yy] = FALSE; /* do not replace border with space */
11243 if (!IN_LEV_FIELD(ex, ey))
11245 can_replace[xx][yy] = FALSE;
11246 complete_replace = FALSE;
11253 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11254 e = MovingOrBlocked2Element(ex, ey);
11256 is_empty = (IS_FREE(ex, ey) ||
11257 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11259 is_walkable = (is_empty || IS_WALKABLE(e));
11260 is_diggable = (is_empty || IS_DIGGABLE(e));
11261 is_collectible = (is_empty || IS_COLLECTIBLE(e));
11262 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11263 is_removable = (is_diggable || is_collectible);
11265 can_replace[xx][yy] =
11266 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
11267 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
11268 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
11269 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
11270 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
11271 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11272 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11274 if (!can_replace[xx][yy])
11275 complete_replace = FALSE;
11278 if (!change->only_if_complete || complete_replace)
11280 boolean something_has_changed = FALSE;
11282 if (change->only_if_complete && change->use_random_replace &&
11283 RND(100) < change->random_percentage)
11286 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11288 int ex = x + xx - 1;
11289 int ey = y + yy - 1;
11290 int content_element;
11292 if (can_replace[xx][yy] && (!change->use_random_replace ||
11293 RND(100) < change->random_percentage))
11295 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11296 RemoveMovingField(ex, ey);
11298 ChangeEvent[ex][ey] = ChangeEvent[x][y];
11300 content_element = change->target_content.e[xx][yy];
11301 target_element = GET_TARGET_ELEMENT(element, content_element, change,
11302 ce_value, ce_score);
11304 CreateElementFromChange(ex, ey, target_element);
11306 something_has_changed = TRUE;
11308 /* for symmetry reasons, freeze newly created border elements */
11309 if (ex != x || ey != y)
11310 Stop[ex][ey] = TRUE; /* no more moving in this frame */
11314 if (something_has_changed)
11316 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11317 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11323 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11324 ce_value, ce_score);
11326 if (element == EL_DIAGONAL_GROWING ||
11327 element == EL_DIAGONAL_SHRINKING)
11329 target_element = Store[x][y];
11331 Store[x][y] = EL_EMPTY;
11334 CreateElementFromChange(x, y, target_element);
11336 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11337 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11340 /* this uses direct change before indirect change */
11341 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11346 #if USE_NEW_DELAYED_ACTION
11348 static void HandleElementChange(int x, int y, int page)
11350 int element = MovingOrBlocked2Element(x, y);
11351 struct ElementInfo *ei = &element_info[element];
11352 struct ElementChangeInfo *change = &ei->change_page[page];
11353 boolean handle_action_before_change = FALSE;
11356 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11357 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11360 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11361 x, y, element, element_info[element].token_name);
11362 printf("HandleElementChange(): This should never happen!\n");
11367 /* this can happen with classic bombs on walkable, changing elements */
11368 if (!CAN_CHANGE_OR_HAS_ACTION(element))
11371 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11372 ChangeDelay[x][y] = 0;
11378 if (ChangeDelay[x][y] == 0) /* initialize element change */
11380 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11382 if (change->can_change)
11385 /* !!! not clear why graphic animation should be reset at all here !!! */
11386 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11387 #if USE_GFX_RESET_WHEN_NOT_MOVING
11388 /* when a custom element is about to change (for example by change delay),
11389 do not reset graphic animation when the custom element is moving */
11390 if (!IS_MOVING(x, y))
11393 ResetGfxAnimation(x, y);
11394 ResetRandomAnimationValue(x, y);
11398 if (change->pre_change_function)
11399 change->pre_change_function(x, y);
11403 ChangeDelay[x][y]--;
11405 if (ChangeDelay[x][y] != 0) /* continue element change */
11407 if (change->can_change)
11409 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11411 if (IS_ANIMATED(graphic))
11412 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11414 if (change->change_function)
11415 change->change_function(x, y);
11418 else /* finish element change */
11420 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11422 page = ChangePage[x][y];
11423 ChangePage[x][y] = -1;
11425 change = &ei->change_page[page];
11428 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11430 ChangeDelay[x][y] = 1; /* try change after next move step */
11431 ChangePage[x][y] = page; /* remember page to use for change */
11437 /* special case: set new level random seed before changing element */
11438 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11439 handle_action_before_change = TRUE;
11441 if (change->has_action && handle_action_before_change)
11442 ExecuteCustomElementAction(x, y, element, page);
11445 if (change->can_change)
11447 if (ChangeElement(x, y, element, page))
11449 if (change->post_change_function)
11450 change->post_change_function(x, y);
11454 if (change->has_action && !handle_action_before_change)
11455 ExecuteCustomElementAction(x, y, element, page);
11461 static void HandleElementChange(int x, int y, int page)
11463 int element = MovingOrBlocked2Element(x, y);
11464 struct ElementInfo *ei = &element_info[element];
11465 struct ElementChangeInfo *change = &ei->change_page[page];
11468 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11471 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11472 x, y, element, element_info[element].token_name);
11473 printf("HandleElementChange(): This should never happen!\n");
11478 /* this can happen with classic bombs on walkable, changing elements */
11479 if (!CAN_CHANGE(element))
11482 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11483 ChangeDelay[x][y] = 0;
11489 if (ChangeDelay[x][y] == 0) /* initialize element change */
11491 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11493 ResetGfxAnimation(x, y);
11494 ResetRandomAnimationValue(x, y);
11496 if (change->pre_change_function)
11497 change->pre_change_function(x, y);
11500 ChangeDelay[x][y]--;
11502 if (ChangeDelay[x][y] != 0) /* continue element change */
11504 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11506 if (IS_ANIMATED(graphic))
11507 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11509 if (change->change_function)
11510 change->change_function(x, y);
11512 else /* finish element change */
11514 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11516 page = ChangePage[x][y];
11517 ChangePage[x][y] = -1;
11519 change = &ei->change_page[page];
11522 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11524 ChangeDelay[x][y] = 1; /* try change after next move step */
11525 ChangePage[x][y] = page; /* remember page to use for change */
11530 if (ChangeElement(x, y, element, page))
11532 if (change->post_change_function)
11533 change->post_change_function(x, y);
11540 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11541 int trigger_element,
11543 int trigger_player,
11547 boolean change_done_any = FALSE;
11548 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11551 if (!(trigger_events[trigger_element][trigger_event]))
11555 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11556 trigger_event, recursion_loop_depth, recursion_loop_detected,
11557 recursion_loop_element, EL_NAME(recursion_loop_element));
11560 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11562 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11564 int element = EL_CUSTOM_START + i;
11565 boolean change_done = FALSE;
11568 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11569 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11572 for (p = 0; p < element_info[element].num_change_pages; p++)
11574 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11576 if (change->can_change_or_has_action &&
11577 change->has_event[trigger_event] &&
11578 change->trigger_side & trigger_side &&
11579 change->trigger_player & trigger_player &&
11580 change->trigger_page & trigger_page_bits &&
11581 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11583 change->actual_trigger_element = trigger_element;
11584 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11585 change->actual_trigger_player_bits = trigger_player;
11586 change->actual_trigger_side = trigger_side;
11587 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11588 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11591 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11592 element, EL_NAME(element), p);
11595 if ((change->can_change && !change_done) || change->has_action)
11599 SCAN_PLAYFIELD(x, y)
11601 if (Feld[x][y] == element)
11603 if (change->can_change && !change_done)
11605 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11606 /* if element already changed in this frame, not only prevent
11607 another element change (checked in ChangeElement()), but
11608 also prevent additional element actions for this element */
11610 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11611 !level.use_action_after_change_bug)
11616 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11617 element, EL_NAME(element), p);
11620 ChangeDelay[x][y] = 1;
11621 ChangeEvent[x][y] = trigger_event;
11623 HandleElementChange(x, y, p);
11625 #if USE_NEW_DELAYED_ACTION
11626 else if (change->has_action)
11628 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11629 /* if element already changed in this frame, not only prevent
11630 another element change (checked in ChangeElement()), but
11631 also prevent additional element actions for this element */
11633 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11634 !level.use_action_after_change_bug)
11640 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11641 element, EL_NAME(element), p);
11644 ExecuteCustomElementAction(x, y, element, p);
11645 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11648 if (change->has_action)
11650 ExecuteCustomElementAction(x, y, element, p);
11651 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11657 if (change->can_change)
11659 change_done = TRUE;
11660 change_done_any = TRUE;
11663 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11664 element, EL_NAME(element), p);
11673 RECURSION_LOOP_DETECTION_END();
11675 return change_done_any;
11678 static boolean CheckElementChangeExt(int x, int y,
11680 int trigger_element,
11682 int trigger_player,
11685 boolean change_done = FALSE;
11688 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11689 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11692 if (Feld[x][y] == EL_BLOCKED)
11694 Blocked2Moving(x, y, &x, &y);
11695 element = Feld[x][y];
11699 /* check if element has already changed */
11700 if (Feld[x][y] != element)
11703 /* check if element has already changed or is about to change after moving */
11704 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11705 Feld[x][y] != element) ||
11707 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11708 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11709 ChangePage[x][y] != -1)))
11714 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11715 trigger_event, recursion_loop_depth, recursion_loop_detected,
11716 recursion_loop_element, EL_NAME(recursion_loop_element));
11719 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11722 printf("::: X: trigger_player_bits == %d\n", trigger_player);
11725 for (p = 0; p < element_info[element].num_change_pages; p++)
11727 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11729 /* check trigger element for all events where the element that is checked
11730 for changing interacts with a directly adjacent element -- this is
11731 different to element changes that affect other elements to change on the
11732 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11733 boolean check_trigger_element =
11734 (trigger_event == CE_TOUCHING_X ||
11735 trigger_event == CE_HITTING_X ||
11736 trigger_event == CE_HIT_BY_X ||
11738 /* this one was forgotten until 3.2.3 */
11739 trigger_event == CE_DIGGING_X);
11742 if (change->can_change_or_has_action &&
11743 change->has_event[trigger_event] &&
11744 change->trigger_side & trigger_side &&
11745 change->trigger_player & trigger_player &&
11746 (!check_trigger_element ||
11747 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11749 change->actual_trigger_element = trigger_element;
11750 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11751 change->actual_trigger_player_bits = trigger_player;
11752 change->actual_trigger_side = trigger_side;
11753 change->actual_trigger_ce_value = CustomValue[x][y];
11754 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11756 /* special case: trigger element not at (x,y) position for some events */
11757 if (check_trigger_element)
11769 { 0, 0 }, { 0, 0 }, { 0, 0 },
11773 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11774 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11776 change->actual_trigger_ce_value = CustomValue[xx][yy];
11777 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11780 if (change->can_change && !change_done)
11782 ChangeDelay[x][y] = 1;
11783 ChangeEvent[x][y] = trigger_event;
11785 HandleElementChange(x, y, p);
11787 change_done = TRUE;
11789 #if USE_NEW_DELAYED_ACTION
11790 else if (change->has_action)
11792 ExecuteCustomElementAction(x, y, element, p);
11793 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11796 if (change->has_action)
11798 ExecuteCustomElementAction(x, y, element, p);
11799 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11805 RECURSION_LOOP_DETECTION_END();
11807 return change_done;
11810 static void PlayPlayerSound(struct PlayerInfo *player)
11812 int jx = player->jx, jy = player->jy;
11813 int sound_element = player->artwork_element;
11814 int last_action = player->last_action_waiting;
11815 int action = player->action_waiting;
11817 if (player->is_waiting)
11819 if (action != last_action)
11820 PlayLevelSoundElementAction(jx, jy, sound_element, action);
11822 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11826 if (action != last_action)
11827 StopSound(element_info[sound_element].sound[last_action]);
11829 if (last_action == ACTION_SLEEPING)
11830 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11834 static void PlayAllPlayersSound()
11838 for (i = 0; i < MAX_PLAYERS; i++)
11839 if (stored_player[i].active)
11840 PlayPlayerSound(&stored_player[i]);
11843 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11845 boolean last_waiting = player->is_waiting;
11846 int move_dir = player->MovDir;
11848 player->dir_waiting = move_dir;
11849 player->last_action_waiting = player->action_waiting;
11853 if (!last_waiting) /* not waiting -> waiting */
11855 player->is_waiting = TRUE;
11857 player->frame_counter_bored =
11859 game.player_boring_delay_fixed +
11860 GetSimpleRandom(game.player_boring_delay_random);
11861 player->frame_counter_sleeping =
11863 game.player_sleeping_delay_fixed +
11864 GetSimpleRandom(game.player_sleeping_delay_random);
11866 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11869 if (game.player_sleeping_delay_fixed +
11870 game.player_sleeping_delay_random > 0 &&
11871 player->anim_delay_counter == 0 &&
11872 player->post_delay_counter == 0 &&
11873 FrameCounter >= player->frame_counter_sleeping)
11874 player->is_sleeping = TRUE;
11875 else if (game.player_boring_delay_fixed +
11876 game.player_boring_delay_random > 0 &&
11877 FrameCounter >= player->frame_counter_bored)
11878 player->is_bored = TRUE;
11880 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11881 player->is_bored ? ACTION_BORING :
11884 if (player->is_sleeping && player->use_murphy)
11886 /* special case for sleeping Murphy when leaning against non-free tile */
11888 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11889 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11890 !IS_MOVING(player->jx - 1, player->jy)))
11891 move_dir = MV_LEFT;
11892 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11893 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11894 !IS_MOVING(player->jx + 1, player->jy)))
11895 move_dir = MV_RIGHT;
11897 player->is_sleeping = FALSE;
11899 player->dir_waiting = move_dir;
11902 if (player->is_sleeping)
11904 if (player->num_special_action_sleeping > 0)
11906 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11908 int last_special_action = player->special_action_sleeping;
11909 int num_special_action = player->num_special_action_sleeping;
11910 int special_action =
11911 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11912 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11913 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11914 last_special_action + 1 : ACTION_SLEEPING);
11915 int special_graphic =
11916 el_act_dir2img(player->artwork_element, special_action, move_dir);
11918 player->anim_delay_counter =
11919 graphic_info[special_graphic].anim_delay_fixed +
11920 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11921 player->post_delay_counter =
11922 graphic_info[special_graphic].post_delay_fixed +
11923 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11925 player->special_action_sleeping = special_action;
11928 if (player->anim_delay_counter > 0)
11930 player->action_waiting = player->special_action_sleeping;
11931 player->anim_delay_counter--;
11933 else if (player->post_delay_counter > 0)
11935 player->post_delay_counter--;
11939 else if (player->is_bored)
11941 if (player->num_special_action_bored > 0)
11943 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11945 int special_action =
11946 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11947 int special_graphic =
11948 el_act_dir2img(player->artwork_element, special_action, move_dir);
11950 player->anim_delay_counter =
11951 graphic_info[special_graphic].anim_delay_fixed +
11952 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11953 player->post_delay_counter =
11954 graphic_info[special_graphic].post_delay_fixed +
11955 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11957 player->special_action_bored = special_action;
11960 if (player->anim_delay_counter > 0)
11962 player->action_waiting = player->special_action_bored;
11963 player->anim_delay_counter--;
11965 else if (player->post_delay_counter > 0)
11967 player->post_delay_counter--;
11972 else if (last_waiting) /* waiting -> not waiting */
11974 player->is_waiting = FALSE;
11975 player->is_bored = FALSE;
11976 player->is_sleeping = FALSE;
11978 player->frame_counter_bored = -1;
11979 player->frame_counter_sleeping = -1;
11981 player->anim_delay_counter = 0;
11982 player->post_delay_counter = 0;
11984 player->dir_waiting = player->MovDir;
11985 player->action_waiting = ACTION_DEFAULT;
11987 player->special_action_bored = ACTION_DEFAULT;
11988 player->special_action_sleeping = ACTION_DEFAULT;
11992 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11994 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11995 int left = player_action & JOY_LEFT;
11996 int right = player_action & JOY_RIGHT;
11997 int up = player_action & JOY_UP;
11998 int down = player_action & JOY_DOWN;
11999 int button1 = player_action & JOY_BUTTON_1;
12000 int button2 = player_action & JOY_BUTTON_2;
12001 int dx = (left ? -1 : right ? 1 : 0);
12002 int dy = (up ? -1 : down ? 1 : 0);
12004 if (!player->active || tape.pausing)
12010 snapped = SnapField(player, dx, dy);
12014 dropped = DropElement(player);
12016 moved = MovePlayer(player, dx, dy);
12019 if (tape.single_step && tape.recording && !tape.pausing)
12022 /* as it is called "single step mode", just return to pause mode when the
12023 player stopped moving after one tile (or never starts moving at all) */
12024 if (!player->is_moving)
12026 /* this is buggy: there are quite some cases where the single step mode
12027 does not return to pause mode (like pushing things that don't move
12028 or simply by trying to run against a wall) */
12029 if (button1 || (dropped && !moved))
12032 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12033 SnapField(player, 0, 0); /* stop snapping */
12037 SetPlayerWaiting(player, FALSE);
12039 return player_action;
12043 /* no actions for this player (no input at player's configured device) */
12045 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
12046 SnapField(player, 0, 0);
12047 CheckGravityMovementWhenNotMoving(player);
12049 if (player->MovPos == 0)
12050 SetPlayerWaiting(player, TRUE);
12052 if (player->MovPos == 0) /* needed for tape.playing */
12053 player->is_moving = FALSE;
12055 player->is_dropping = FALSE;
12056 player->is_dropping_pressed = FALSE;
12057 player->drop_pressed_delay = 0;
12063 static void CheckLevelTime()
12067 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
12068 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12070 if (level.native_em_level->lev->home == 0) /* all players at home */
12072 PlayerWins(local_player);
12074 AllPlayersGone = TRUE;
12076 level.native_em_level->lev->home = -1;
12079 if (level.native_em_level->ply[0]->alive == 0 &&
12080 level.native_em_level->ply[1]->alive == 0 &&
12081 level.native_em_level->ply[2]->alive == 0 &&
12082 level.native_em_level->ply[3]->alive == 0) /* all dead */
12083 AllPlayersGone = TRUE;
12085 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12087 if (game_sp.LevelSolved &&
12088 !game_sp.GameOver) /* game won */
12090 PlayerWins(local_player);
12092 game_sp.GameOver = TRUE;
12094 AllPlayersGone = TRUE;
12097 if (game_sp.GameOver) /* game lost */
12098 AllPlayersGone = TRUE;
12101 if (TimeFrames >= FRAMES_PER_SECOND)
12106 for (i = 0; i < MAX_PLAYERS; i++)
12108 struct PlayerInfo *player = &stored_player[i];
12110 if (SHIELD_ON(player))
12112 player->shield_normal_time_left--;
12114 if (player->shield_deadly_time_left > 0)
12115 player->shield_deadly_time_left--;
12119 if (!local_player->LevelSolved && !level.use_step_counter)
12127 if (TimeLeft <= 10 && setup.time_limit)
12128 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12131 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12133 DisplayGameControlValues();
12135 DrawGameValue_Time(TimeLeft);
12138 if (!TimeLeft && setup.time_limit)
12140 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12141 level.native_em_level->lev->killed_out_of_time = TRUE;
12143 for (i = 0; i < MAX_PLAYERS; i++)
12144 KillPlayer(&stored_player[i]);
12148 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12150 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12152 DisplayGameControlValues();
12155 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12156 DrawGameValue_Time(TimePlayed);
12159 level.native_em_level->lev->time =
12160 (level.time == 0 ? TimePlayed : TimeLeft);
12163 if (tape.recording || tape.playing)
12164 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12168 UpdateAndDisplayGameControlValues();
12170 UpdateGameDoorValues();
12171 DrawGameDoorValues();
12175 void AdvanceFrameAndPlayerCounters(int player_nr)
12179 /* advance frame counters (global frame counter and time frame counter) */
12183 /* advance player counters (counters for move delay, move animation etc.) */
12184 for (i = 0; i < MAX_PLAYERS; i++)
12186 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12187 int move_delay_value = stored_player[i].move_delay_value;
12188 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12190 if (!advance_player_counters) /* not all players may be affected */
12193 #if USE_NEW_PLAYER_ANIM
12194 if (move_frames == 0) /* less than one move per game frame */
12196 int stepsize = TILEX / move_delay_value;
12197 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12198 int count = (stored_player[i].is_moving ?
12199 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12201 if (count % delay == 0)
12206 stored_player[i].Frame += move_frames;
12208 if (stored_player[i].MovPos != 0)
12209 stored_player[i].StepFrame += move_frames;
12211 if (stored_player[i].move_delay > 0)
12212 stored_player[i].move_delay--;
12214 /* due to bugs in previous versions, counter must count up, not down */
12215 if (stored_player[i].push_delay != -1)
12216 stored_player[i].push_delay++;
12218 if (stored_player[i].drop_delay > 0)
12219 stored_player[i].drop_delay--;
12221 if (stored_player[i].is_dropping_pressed)
12222 stored_player[i].drop_pressed_delay++;
12226 void StartGameActions(boolean init_network_game, boolean record_tape,
12229 unsigned long new_random_seed = InitRND(random_seed);
12232 TapeStartRecording(new_random_seed);
12234 #if defined(NETWORK_AVALIABLE)
12235 if (init_network_game)
12237 SendToServer_StartPlaying();
12248 static unsigned long game_frame_delay = 0;
12249 unsigned long game_frame_delay_value;
12250 byte *recorded_player_action;
12251 byte summarized_player_action = 0;
12252 byte tape_action[MAX_PLAYERS];
12255 /* detect endless loops, caused by custom element programming */
12256 if (recursion_loop_detected && recursion_loop_depth == 0)
12258 char *message = getStringCat3("Internal Error ! Element ",
12259 EL_NAME(recursion_loop_element),
12260 " caused endless loop ! Quit the game ?");
12262 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12263 EL_NAME(recursion_loop_element));
12265 RequestQuitGameExt(FALSE, level_editor_test_game, message);
12267 recursion_loop_detected = FALSE; /* if game should be continued */
12274 if (game.restart_level)
12275 StartGameActions(options.network, setup.autorecord, level.random_seed);
12277 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
12278 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12280 if (level.native_em_level->lev->home == 0) /* all players at home */
12282 PlayerWins(local_player);
12284 AllPlayersGone = TRUE;
12286 level.native_em_level->lev->home = -1;
12289 if (level.native_em_level->ply[0]->alive == 0 &&
12290 level.native_em_level->ply[1]->alive == 0 &&
12291 level.native_em_level->ply[2]->alive == 0 &&
12292 level.native_em_level->ply[3]->alive == 0) /* all dead */
12293 AllPlayersGone = TRUE;
12295 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12297 if (game_sp.LevelSolved &&
12298 !game_sp.GameOver) /* game won */
12300 PlayerWins(local_player);
12302 game_sp.GameOver = TRUE;
12304 AllPlayersGone = TRUE;
12307 if (game_sp.GameOver) /* game lost */
12308 AllPlayersGone = TRUE;
12311 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12314 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12317 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
12320 game_frame_delay_value =
12321 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12323 if (tape.playing && tape.warp_forward && !tape.pausing)
12324 game_frame_delay_value = 0;
12326 /* ---------- main game synchronization point ---------- */
12328 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12330 if (network_playing && !network_player_action_received)
12332 /* try to get network player actions in time */
12334 #if defined(NETWORK_AVALIABLE)
12335 /* last chance to get network player actions without main loop delay */
12336 HandleNetworking();
12339 /* game was quit by network peer */
12340 if (game_status != GAME_MODE_PLAYING)
12343 if (!network_player_action_received)
12344 return; /* failed to get network player actions in time */
12346 /* do not yet reset "network_player_action_received" (for tape.pausing) */
12352 /* at this point we know that we really continue executing the game */
12354 network_player_action_received = FALSE;
12356 /* when playing tape, read previously recorded player input from tape data */
12357 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12360 /* TapePlayAction() may return NULL when toggling to "pause before death" */
12365 if (tape.set_centered_player)
12367 game.centered_player_nr_next = tape.centered_player_nr_next;
12368 game.set_centered_player = TRUE;
12371 for (i = 0; i < MAX_PLAYERS; i++)
12373 summarized_player_action |= stored_player[i].action;
12375 if (!network_playing)
12376 stored_player[i].effective_action = stored_player[i].action;
12379 #if defined(NETWORK_AVALIABLE)
12380 if (network_playing)
12381 SendToServer_MovePlayer(summarized_player_action);
12384 if (!options.network && !setup.team_mode)
12385 local_player->effective_action = summarized_player_action;
12387 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
12389 for (i = 0; i < MAX_PLAYERS; i++)
12390 stored_player[i].effective_action =
12391 (i == game.centered_player_nr ? summarized_player_action : 0);
12394 if (recorded_player_action != NULL)
12395 for (i = 0; i < MAX_PLAYERS; i++)
12396 stored_player[i].effective_action = recorded_player_action[i];
12398 for (i = 0; i < MAX_PLAYERS; i++)
12400 tape_action[i] = stored_player[i].effective_action;
12402 /* (this can only happen in the R'n'D game engine) */
12403 if (tape.recording && tape_action[i] && !tape.player_participates[i])
12404 tape.player_participates[i] = TRUE; /* player just appeared from CE */
12407 /* only record actions from input devices, but not programmed actions */
12408 if (tape.recording)
12409 TapeRecordAction(tape_action);
12411 #if USE_NEW_PLAYER_ASSIGNMENTS
12413 byte mapped_action[MAX_PLAYERS];
12415 for (i = 0; i < MAX_PLAYERS; i++)
12416 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12418 for (i = 0; i < MAX_PLAYERS; i++)
12419 stored_player[i].effective_action = mapped_action[i];
12423 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12425 GameActions_EM_Main();
12427 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12429 GameActions_SP_Main();
12437 void GameActions_EM_Main()
12439 byte effective_action[MAX_PLAYERS];
12440 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12443 for (i = 0; i < MAX_PLAYERS; i++)
12444 effective_action[i] = stored_player[i].effective_action;
12446 GameActions_EM(effective_action, warp_mode);
12450 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12453 void GameActions_SP_Main()
12455 byte effective_action[MAX_PLAYERS];
12456 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12459 for (i = 0; i < MAX_PLAYERS; i++)
12460 effective_action[i] = stored_player[i].effective_action;
12462 GameActions_SP(effective_action, warp_mode);
12466 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12469 void GameActions_RND()
12471 int magic_wall_x = 0, magic_wall_y = 0;
12472 int i, x, y, element, graphic;
12474 InitPlayfieldScanModeVars();
12476 #if USE_ONE_MORE_CHANGE_PER_FRAME
12477 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12479 SCAN_PLAYFIELD(x, y)
12481 ChangeCount[x][y] = 0;
12482 ChangeEvent[x][y] = -1;
12487 if (game.set_centered_player)
12489 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12491 /* switching to "all players" only possible if all players fit to screen */
12492 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12494 game.centered_player_nr_next = game.centered_player_nr;
12495 game.set_centered_player = FALSE;
12498 /* do not switch focus to non-existing (or non-active) player */
12499 if (game.centered_player_nr_next >= 0 &&
12500 !stored_player[game.centered_player_nr_next].active)
12502 game.centered_player_nr_next = game.centered_player_nr;
12503 game.set_centered_player = FALSE;
12507 if (game.set_centered_player &&
12508 ScreenMovPos == 0) /* screen currently aligned at tile position */
12512 if (game.centered_player_nr_next == -1)
12514 setScreenCenteredToAllPlayers(&sx, &sy);
12518 sx = stored_player[game.centered_player_nr_next].jx;
12519 sy = stored_player[game.centered_player_nr_next].jy;
12522 game.centered_player_nr = game.centered_player_nr_next;
12523 game.set_centered_player = FALSE;
12525 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12526 DrawGameDoorValues();
12529 for (i = 0; i < MAX_PLAYERS; i++)
12531 int actual_player_action = stored_player[i].effective_action;
12534 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12535 - rnd_equinox_tetrachloride 048
12536 - rnd_equinox_tetrachloride_ii 096
12537 - rnd_emanuel_schmieg 002
12538 - doctor_sloan_ww 001, 020
12540 if (stored_player[i].MovPos == 0)
12541 CheckGravityMovement(&stored_player[i]);
12544 /* overwrite programmed action with tape action */
12545 if (stored_player[i].programmed_action)
12546 actual_player_action = stored_player[i].programmed_action;
12548 PlayerActions(&stored_player[i], actual_player_action);
12550 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12553 ScrollScreen(NULL, SCROLL_GO_ON);
12555 /* for backwards compatibility, the following code emulates a fixed bug that
12556 occured when pushing elements (causing elements that just made their last
12557 pushing step to already (if possible) make their first falling step in the
12558 same game frame, which is bad); this code is also needed to use the famous
12559 "spring push bug" which is used in older levels and might be wanted to be
12560 used also in newer levels, but in this case the buggy pushing code is only
12561 affecting the "spring" element and no other elements */
12563 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12565 for (i = 0; i < MAX_PLAYERS; i++)
12567 struct PlayerInfo *player = &stored_player[i];
12568 int x = player->jx;
12569 int y = player->jy;
12571 if (player->active && player->is_pushing && player->is_moving &&
12573 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12574 Feld[x][y] == EL_SPRING))
12576 ContinueMoving(x, y);
12578 /* continue moving after pushing (this is actually a bug) */
12579 if (!IS_MOVING(x, y))
12580 Stop[x][y] = FALSE;
12586 debug_print_timestamp(0, "start main loop profiling");
12589 SCAN_PLAYFIELD(x, y)
12591 ChangeCount[x][y] = 0;
12592 ChangeEvent[x][y] = -1;
12594 /* this must be handled before main playfield loop */
12595 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12598 if (MovDelay[x][y] <= 0)
12602 #if USE_NEW_SNAP_DELAY
12603 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12606 if (MovDelay[x][y] <= 0)
12609 TEST_DrawLevelField(x, y);
12611 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12617 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12619 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12620 printf("GameActions(): This should never happen!\n");
12622 ChangePage[x][y] = -1;
12626 Stop[x][y] = FALSE;
12627 if (WasJustMoving[x][y] > 0)
12628 WasJustMoving[x][y]--;
12629 if (WasJustFalling[x][y] > 0)
12630 WasJustFalling[x][y]--;
12631 if (CheckCollision[x][y] > 0)
12632 CheckCollision[x][y]--;
12633 if (CheckImpact[x][y] > 0)
12634 CheckImpact[x][y]--;
12638 /* reset finished pushing action (not done in ContinueMoving() to allow
12639 continuous pushing animation for elements with zero push delay) */
12640 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12642 ResetGfxAnimation(x, y);
12643 TEST_DrawLevelField(x, y);
12647 if (IS_BLOCKED(x, y))
12651 Blocked2Moving(x, y, &oldx, &oldy);
12652 if (!IS_MOVING(oldx, oldy))
12654 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12655 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12656 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12657 printf("GameActions(): This should never happen!\n");
12664 debug_print_timestamp(0, "- time for pre-main loop:");
12667 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
12668 SCAN_PLAYFIELD(x, y)
12670 element = Feld[x][y];
12671 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12676 int element2 = element;
12677 int graphic2 = graphic;
12679 int element2 = Feld[x][y];
12680 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12682 int last_gfx_frame = GfxFrame[x][y];
12684 if (graphic_info[graphic2].anim_global_sync)
12685 GfxFrame[x][y] = FrameCounter;
12686 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12687 GfxFrame[x][y] = CustomValue[x][y];
12688 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12689 GfxFrame[x][y] = element_info[element2].collect_score;
12690 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12691 GfxFrame[x][y] = ChangeDelay[x][y];
12693 if (redraw && GfxFrame[x][y] != last_gfx_frame)
12694 DrawLevelGraphicAnimation(x, y, graphic2);
12697 ResetGfxFrame(x, y, TRUE);
12701 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12702 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12703 ResetRandomAnimationValue(x, y);
12707 SetRandomAnimationValue(x, y);
12711 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12714 #endif // -------------------- !!! TEST ONLY !!! --------------------
12717 debug_print_timestamp(0, "- time for TEST loop: -->");
12720 SCAN_PLAYFIELD(x, y)
12722 element = Feld[x][y];
12723 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12725 ResetGfxFrame(x, y, TRUE);
12727 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12728 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12729 ResetRandomAnimationValue(x, y);
12731 SetRandomAnimationValue(x, y);
12733 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12735 if (IS_INACTIVE(element))
12737 if (IS_ANIMATED(graphic))
12738 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12743 /* this may take place after moving, so 'element' may have changed */
12744 if (IS_CHANGING(x, y) &&
12745 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12747 int page = element_info[element].event_page_nr[CE_DELAY];
12750 HandleElementChange(x, y, page);
12752 if (CAN_CHANGE(element))
12753 HandleElementChange(x, y, page);
12755 if (HAS_ACTION(element))
12756 ExecuteCustomElementAction(x, y, element, page);
12759 element = Feld[x][y];
12760 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12763 #if 0 // ---------------------------------------------------------------------
12765 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12769 element = Feld[x][y];
12770 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12772 if (IS_ANIMATED(graphic) &&
12773 !IS_MOVING(x, y) &&
12775 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12777 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12778 TEST_DrawTwinkleOnField(x, y);
12780 else if (IS_MOVING(x, y))
12781 ContinueMoving(x, y);
12788 case EL_EM_EXIT_OPEN:
12789 case EL_SP_EXIT_OPEN:
12790 case EL_STEEL_EXIT_OPEN:
12791 case EL_EM_STEEL_EXIT_OPEN:
12792 case EL_SP_TERMINAL:
12793 case EL_SP_TERMINAL_ACTIVE:
12794 case EL_EXTRA_TIME:
12795 case EL_SHIELD_NORMAL:
12796 case EL_SHIELD_DEADLY:
12797 if (IS_ANIMATED(graphic))
12798 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12801 case EL_DYNAMITE_ACTIVE:
12802 case EL_EM_DYNAMITE_ACTIVE:
12803 case EL_DYNABOMB_PLAYER_1_ACTIVE:
12804 case EL_DYNABOMB_PLAYER_2_ACTIVE:
12805 case EL_DYNABOMB_PLAYER_3_ACTIVE:
12806 case EL_DYNABOMB_PLAYER_4_ACTIVE:
12807 case EL_SP_DISK_RED_ACTIVE:
12808 CheckDynamite(x, y);
12811 case EL_AMOEBA_GROWING:
12812 AmoebeWaechst(x, y);
12815 case EL_AMOEBA_SHRINKING:
12816 AmoebaDisappearing(x, y);
12819 #if !USE_NEW_AMOEBA_CODE
12820 case EL_AMOEBA_WET:
12821 case EL_AMOEBA_DRY:
12822 case EL_AMOEBA_FULL:
12824 case EL_EMC_DRIPPER:
12825 AmoebeAbleger(x, y);
12829 case EL_GAME_OF_LIFE:
12834 case EL_EXIT_CLOSED:
12838 case EL_EM_EXIT_CLOSED:
12842 case EL_STEEL_EXIT_CLOSED:
12843 CheckExitSteel(x, y);
12846 case EL_EM_STEEL_EXIT_CLOSED:
12847 CheckExitSteelEM(x, y);
12850 case EL_SP_EXIT_CLOSED:
12854 case EL_EXPANDABLE_WALL_GROWING:
12855 case EL_EXPANDABLE_STEELWALL_GROWING:
12856 MauerWaechst(x, y);
12859 case EL_EXPANDABLE_WALL:
12860 case EL_EXPANDABLE_WALL_HORIZONTAL:
12861 case EL_EXPANDABLE_WALL_VERTICAL:
12862 case EL_EXPANDABLE_WALL_ANY:
12863 case EL_BD_EXPANDABLE_WALL:
12864 MauerAbleger(x, y);
12867 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12868 case EL_EXPANDABLE_STEELWALL_VERTICAL:
12869 case EL_EXPANDABLE_STEELWALL_ANY:
12870 MauerAblegerStahl(x, y);
12874 CheckForDragon(x, y);
12880 case EL_ELEMENT_SNAPPING:
12881 case EL_DIAGONAL_SHRINKING:
12882 case EL_DIAGONAL_GROWING:
12885 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12887 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12892 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12893 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12898 #else // ---------------------------------------------------------------------
12900 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12904 element = Feld[x][y];
12905 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12907 if (IS_ANIMATED(graphic) &&
12908 !IS_MOVING(x, y) &&
12910 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12912 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12913 TEST_DrawTwinkleOnField(x, y);
12915 else if ((element == EL_ACID ||
12916 element == EL_EXIT_OPEN ||
12917 element == EL_EM_EXIT_OPEN ||
12918 element == EL_SP_EXIT_OPEN ||
12919 element == EL_STEEL_EXIT_OPEN ||
12920 element == EL_EM_STEEL_EXIT_OPEN ||
12921 element == EL_SP_TERMINAL ||
12922 element == EL_SP_TERMINAL_ACTIVE ||
12923 element == EL_EXTRA_TIME ||
12924 element == EL_SHIELD_NORMAL ||
12925 element == EL_SHIELD_DEADLY) &&
12926 IS_ANIMATED(graphic))
12927 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12928 else if (IS_MOVING(x, y))
12929 ContinueMoving(x, y);
12930 else if (IS_ACTIVE_BOMB(element))
12931 CheckDynamite(x, y);
12932 else if (element == EL_AMOEBA_GROWING)
12933 AmoebeWaechst(x, y);
12934 else if (element == EL_AMOEBA_SHRINKING)
12935 AmoebaDisappearing(x, y);
12937 #if !USE_NEW_AMOEBA_CODE
12938 else if (IS_AMOEBALIVE(element))
12939 AmoebeAbleger(x, y);
12942 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12944 else if (element == EL_EXIT_CLOSED)
12946 else if (element == EL_EM_EXIT_CLOSED)
12948 else if (element == EL_STEEL_EXIT_CLOSED)
12949 CheckExitSteel(x, y);
12950 else if (element == EL_EM_STEEL_EXIT_CLOSED)
12951 CheckExitSteelEM(x, y);
12952 else if (element == EL_SP_EXIT_CLOSED)
12954 else if (element == EL_EXPANDABLE_WALL_GROWING ||
12955 element == EL_EXPANDABLE_STEELWALL_GROWING)
12956 MauerWaechst(x, y);
12957 else if (element == EL_EXPANDABLE_WALL ||
12958 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12959 element == EL_EXPANDABLE_WALL_VERTICAL ||
12960 element == EL_EXPANDABLE_WALL_ANY ||
12961 element == EL_BD_EXPANDABLE_WALL)
12962 MauerAbleger(x, y);
12963 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12964 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12965 element == EL_EXPANDABLE_STEELWALL_ANY)
12966 MauerAblegerStahl(x, y);
12967 else if (element == EL_FLAMES)
12968 CheckForDragon(x, y);
12969 else if (element == EL_EXPLOSION)
12970 ; /* drawing of correct explosion animation is handled separately */
12971 else if (element == EL_ELEMENT_SNAPPING ||
12972 element == EL_DIAGONAL_SHRINKING ||
12973 element == EL_DIAGONAL_GROWING)
12975 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12977 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12979 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12980 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12982 #endif // ---------------------------------------------------------------------
12984 if (IS_BELT_ACTIVE(element))
12985 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12987 if (game.magic_wall_active)
12989 int jx = local_player->jx, jy = local_player->jy;
12991 /* play the element sound at the position nearest to the player */
12992 if ((element == EL_MAGIC_WALL_FULL ||
12993 element == EL_MAGIC_WALL_ACTIVE ||
12994 element == EL_MAGIC_WALL_EMPTYING ||
12995 element == EL_BD_MAGIC_WALL_FULL ||
12996 element == EL_BD_MAGIC_WALL_ACTIVE ||
12997 element == EL_BD_MAGIC_WALL_EMPTYING ||
12998 element == EL_DC_MAGIC_WALL_FULL ||
12999 element == EL_DC_MAGIC_WALL_ACTIVE ||
13000 element == EL_DC_MAGIC_WALL_EMPTYING) &&
13001 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
13010 debug_print_timestamp(0, "- time for MAIN loop: -->");
13013 #if USE_NEW_AMOEBA_CODE
13014 /* new experimental amoeba growth stuff */
13015 if (!(FrameCounter % 8))
13017 static unsigned long random = 1684108901;
13019 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
13021 x = RND(lev_fieldx);
13022 y = RND(lev_fieldy);
13023 element = Feld[x][y];
13025 if (!IS_PLAYER(x,y) &&
13026 (element == EL_EMPTY ||
13027 CAN_GROW_INTO(element) ||
13028 element == EL_QUICKSAND_EMPTY ||
13029 element == EL_QUICKSAND_FAST_EMPTY ||
13030 element == EL_ACID_SPLASH_LEFT ||
13031 element == EL_ACID_SPLASH_RIGHT))
13033 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
13034 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
13035 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
13036 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
13037 Feld[x][y] = EL_AMOEBA_DROP;
13040 random = random * 129 + 1;
13046 if (game.explosions_delayed)
13049 game.explosions_delayed = FALSE;
13051 SCAN_PLAYFIELD(x, y)
13053 element = Feld[x][y];
13055 if (ExplodeField[x][y])
13056 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
13057 else if (element == EL_EXPLOSION)
13058 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
13060 ExplodeField[x][y] = EX_TYPE_NONE;
13063 game.explosions_delayed = TRUE;
13066 if (game.magic_wall_active)
13068 if (!(game.magic_wall_time_left % 4))
13070 int element = Feld[magic_wall_x][magic_wall_y];
13072 if (element == EL_BD_MAGIC_WALL_FULL ||
13073 element == EL_BD_MAGIC_WALL_ACTIVE ||
13074 element == EL_BD_MAGIC_WALL_EMPTYING)
13075 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
13076 else if (element == EL_DC_MAGIC_WALL_FULL ||
13077 element == EL_DC_MAGIC_WALL_ACTIVE ||
13078 element == EL_DC_MAGIC_WALL_EMPTYING)
13079 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
13081 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
13084 if (game.magic_wall_time_left > 0)
13086 game.magic_wall_time_left--;
13088 if (!game.magic_wall_time_left)
13090 SCAN_PLAYFIELD(x, y)
13092 element = Feld[x][y];
13094 if (element == EL_MAGIC_WALL_ACTIVE ||
13095 element == EL_MAGIC_WALL_FULL)
13097 Feld[x][y] = EL_MAGIC_WALL_DEAD;
13098 TEST_DrawLevelField(x, y);
13100 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
13101 element == EL_BD_MAGIC_WALL_FULL)
13103 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
13104 TEST_DrawLevelField(x, y);
13106 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
13107 element == EL_DC_MAGIC_WALL_FULL)
13109 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
13110 TEST_DrawLevelField(x, y);
13114 game.magic_wall_active = FALSE;
13119 if (game.light_time_left > 0)
13121 game.light_time_left--;
13123 if (game.light_time_left == 0)
13124 RedrawAllLightSwitchesAndInvisibleElements();
13127 if (game.timegate_time_left > 0)
13129 game.timegate_time_left--;
13131 if (game.timegate_time_left == 0)
13132 CloseAllOpenTimegates();
13135 if (game.lenses_time_left > 0)
13137 game.lenses_time_left--;
13139 if (game.lenses_time_left == 0)
13140 RedrawAllInvisibleElementsForLenses();
13143 if (game.magnify_time_left > 0)
13145 game.magnify_time_left--;
13147 if (game.magnify_time_left == 0)
13148 RedrawAllInvisibleElementsForMagnifier();
13151 for (i = 0; i < MAX_PLAYERS; i++)
13153 struct PlayerInfo *player = &stored_player[i];
13155 if (SHIELD_ON(player))
13157 if (player->shield_deadly_time_left)
13158 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13159 else if (player->shield_normal_time_left)
13160 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13164 #if USE_DELAYED_GFX_REDRAW
13165 SCAN_PLAYFIELD(x, y)
13168 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13170 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13171 GfxRedraw[x][y] != GFX_REDRAW_NONE)
13174 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13175 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13177 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13178 DrawLevelField(x, y);
13180 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13181 DrawLevelFieldCrumbled(x, y);
13183 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13184 DrawLevelFieldCrumbledNeighbours(x, y);
13186 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13187 DrawTwinkleOnField(x, y);
13190 GfxRedraw[x][y] = GFX_REDRAW_NONE;
13197 PlayAllPlayersSound();
13199 if (options.debug) /* calculate frames per second */
13201 static unsigned long fps_counter = 0;
13202 static int fps_frames = 0;
13203 unsigned long fps_delay_ms = Counter() - fps_counter;
13207 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
13209 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13212 fps_counter = Counter();
13215 redraw_mask |= REDRAW_FPS;
13218 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
13220 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13222 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13224 local_player->show_envelope = 0;
13228 debug_print_timestamp(0, "stop main loop profiling ");
13229 printf("----------------------------------------------------------\n");
13232 /* use random number generator in every frame to make it less predictable */
13233 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13237 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13239 int min_x = x, min_y = y, max_x = x, max_y = y;
13242 for (i = 0; i < MAX_PLAYERS; i++)
13244 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13246 if (!stored_player[i].active || &stored_player[i] == player)
13249 min_x = MIN(min_x, jx);
13250 min_y = MIN(min_y, jy);
13251 max_x = MAX(max_x, jx);
13252 max_y = MAX(max_y, jy);
13255 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13258 static boolean AllPlayersInVisibleScreen()
13262 for (i = 0; i < MAX_PLAYERS; i++)
13264 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13266 if (!stored_player[i].active)
13269 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13276 void ScrollLevel(int dx, int dy)
13279 /* (directly solved in BlitBitmap() now) */
13280 static Bitmap *bitmap_db_field2 = NULL;
13281 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13288 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13289 /* only horizontal XOR vertical scroll direction allowed */
13290 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13295 /* (directly solved in BlitBitmap() now) */
13296 if (bitmap_db_field2 == NULL)
13297 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13299 /* needed when blitting directly to same bitmap -- should not be needed with
13300 recent SDL libraries, but apparently does not work in 1.2.11 directly */
13301 BlitBitmap(drawto_field, bitmap_db_field2,
13302 FX + TILEX * (dx == -1) - softscroll_offset,
13303 FY + TILEY * (dy == -1) - softscroll_offset,
13304 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13305 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13306 FX + TILEX * (dx == 1) - softscroll_offset,
13307 FY + TILEY * (dy == 1) - softscroll_offset);
13308 BlitBitmap(bitmap_db_field2, drawto_field,
13309 FX + TILEX * (dx == 1) - softscroll_offset,
13310 FY + TILEY * (dy == 1) - softscroll_offset,
13311 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13312 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13313 FX + TILEX * (dx == 1) - softscroll_offset,
13314 FY + TILEY * (dy == 1) - softscroll_offset);
13319 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13320 int xsize = (BX2 - BX1 + 1);
13321 int ysize = (BY2 - BY1 + 1);
13322 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13323 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13324 int step = (start < end ? +1 : -1);
13326 for (i = start; i != end; i += step)
13328 BlitBitmap(drawto_field, drawto_field,
13329 FX + TILEX * (dx != 0 ? i + step : 0),
13330 FY + TILEY * (dy != 0 ? i + step : 0),
13331 TILEX * (dx != 0 ? 1 : xsize),
13332 TILEY * (dy != 0 ? 1 : ysize),
13333 FX + TILEX * (dx != 0 ? i : 0),
13334 FY + TILEY * (dy != 0 ? i : 0));
13339 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13341 BlitBitmap(drawto_field, drawto_field,
13342 FX + TILEX * (dx == -1) - softscroll_offset,
13343 FY + TILEY * (dy == -1) - softscroll_offset,
13344 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13345 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13346 FX + TILEX * (dx == 1) - softscroll_offset,
13347 FY + TILEY * (dy == 1) - softscroll_offset);
13353 x = (dx == 1 ? BX1 : BX2);
13354 for (y = BY1; y <= BY2; y++)
13355 DrawScreenField(x, y);
13360 y = (dy == 1 ? BY1 : BY2);
13361 for (x = BX1; x <= BX2; x++)
13362 DrawScreenField(x, y);
13365 redraw_mask |= REDRAW_FIELD;
13368 static boolean canFallDown(struct PlayerInfo *player)
13370 int jx = player->jx, jy = player->jy;
13372 return (IN_LEV_FIELD(jx, jy + 1) &&
13373 (IS_FREE(jx, jy + 1) ||
13374 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13375 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13376 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13379 static boolean canPassField(int x, int y, int move_dir)
13381 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13382 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13383 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13384 int nextx = x + dx;
13385 int nexty = y + dy;
13386 int element = Feld[x][y];
13388 return (IS_PASSABLE_FROM(element, opposite_dir) &&
13389 !CAN_MOVE(element) &&
13390 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13391 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13392 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13395 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13397 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13398 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13399 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13403 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13404 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13405 (IS_DIGGABLE(Feld[newx][newy]) ||
13406 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13407 canPassField(newx, newy, move_dir)));
13410 static void CheckGravityMovement(struct PlayerInfo *player)
13412 #if USE_PLAYER_GRAVITY
13413 if (player->gravity && !player->programmed_action)
13415 if (game.gravity && !player->programmed_action)
13418 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13419 int move_dir_vertical = player->effective_action & MV_VERTICAL;
13420 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13421 int jx = player->jx, jy = player->jy;
13422 boolean player_is_moving_to_valid_field =
13423 (!player_is_snapping &&
13424 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13425 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13426 boolean player_can_fall_down = canFallDown(player);
13428 if (player_can_fall_down &&
13429 !player_is_moving_to_valid_field)
13430 player->programmed_action = MV_DOWN;
13434 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13436 return CheckGravityMovement(player);
13438 #if USE_PLAYER_GRAVITY
13439 if (player->gravity && !player->programmed_action)
13441 if (game.gravity && !player->programmed_action)
13444 int jx = player->jx, jy = player->jy;
13445 boolean field_under_player_is_free =
13446 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13447 boolean player_is_standing_on_valid_field =
13448 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13449 (IS_WALKABLE(Feld[jx][jy]) &&
13450 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13452 if (field_under_player_is_free && !player_is_standing_on_valid_field)
13453 player->programmed_action = MV_DOWN;
13458 MovePlayerOneStep()
13459 -----------------------------------------------------------------------------
13460 dx, dy: direction (non-diagonal) to try to move the player to
13461 real_dx, real_dy: direction as read from input device (can be diagonal)
13464 boolean MovePlayerOneStep(struct PlayerInfo *player,
13465 int dx, int dy, int real_dx, int real_dy)
13467 int jx = player->jx, jy = player->jy;
13468 int new_jx = jx + dx, new_jy = jy + dy;
13469 #if !USE_FIXED_DONT_RUN_INTO
13473 boolean player_can_move = !player->cannot_move;
13475 if (!player->active || (!dx && !dy))
13476 return MP_NO_ACTION;
13478 player->MovDir = (dx < 0 ? MV_LEFT :
13479 dx > 0 ? MV_RIGHT :
13481 dy > 0 ? MV_DOWN : MV_NONE);
13483 if (!IN_LEV_FIELD(new_jx, new_jy))
13484 return MP_NO_ACTION;
13486 if (!player_can_move)
13488 if (player->MovPos == 0)
13490 player->is_moving = FALSE;
13491 player->is_digging = FALSE;
13492 player->is_collecting = FALSE;
13493 player->is_snapping = FALSE;
13494 player->is_pushing = FALSE;
13499 if (!options.network && game.centered_player_nr == -1 &&
13500 !AllPlayersInSight(player, new_jx, new_jy))
13501 return MP_NO_ACTION;
13503 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13504 return MP_NO_ACTION;
13507 #if !USE_FIXED_DONT_RUN_INTO
13508 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13510 /* (moved to DigField()) */
13511 if (player_can_move && DONT_RUN_INTO(element))
13513 if (element == EL_ACID && dx == 0 && dy == 1)
13515 SplashAcid(new_jx, new_jy);
13516 Feld[jx][jy] = EL_PLAYER_1;
13517 InitMovingField(jx, jy, MV_DOWN);
13518 Store[jx][jy] = EL_ACID;
13519 ContinueMoving(jx, jy);
13520 BuryPlayer(player);
13523 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13529 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13530 if (can_move != MP_MOVING)
13533 /* check if DigField() has caused relocation of the player */
13534 if (player->jx != jx || player->jy != jy)
13535 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13537 StorePlayer[jx][jy] = 0;
13538 player->last_jx = jx;
13539 player->last_jy = jy;
13540 player->jx = new_jx;
13541 player->jy = new_jy;
13542 StorePlayer[new_jx][new_jy] = player->element_nr;
13544 if (player->move_delay_value_next != -1)
13546 player->move_delay_value = player->move_delay_value_next;
13547 player->move_delay_value_next = -1;
13551 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13553 player->step_counter++;
13555 PlayerVisit[jx][jy] = FrameCounter;
13557 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13558 player->is_moving = TRUE;
13562 /* should better be called in MovePlayer(), but this breaks some tapes */
13563 ScrollPlayer(player, SCROLL_INIT);
13569 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13571 int jx = player->jx, jy = player->jy;
13572 int old_jx = jx, old_jy = jy;
13573 int moved = MP_NO_ACTION;
13575 if (!player->active)
13580 if (player->MovPos == 0)
13582 player->is_moving = FALSE;
13583 player->is_digging = FALSE;
13584 player->is_collecting = FALSE;
13585 player->is_snapping = FALSE;
13586 player->is_pushing = FALSE;
13592 if (player->move_delay > 0)
13595 player->move_delay = -1; /* set to "uninitialized" value */
13597 /* store if player is automatically moved to next field */
13598 player->is_auto_moving = (player->programmed_action != MV_NONE);
13600 /* remove the last programmed player action */
13601 player->programmed_action = 0;
13603 if (player->MovPos)
13605 /* should only happen if pre-1.2 tape recordings are played */
13606 /* this is only for backward compatibility */
13608 int original_move_delay_value = player->move_delay_value;
13611 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
13615 /* scroll remaining steps with finest movement resolution */
13616 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13618 while (player->MovPos)
13620 ScrollPlayer(player, SCROLL_GO_ON);
13621 ScrollScreen(NULL, SCROLL_GO_ON);
13623 AdvanceFrameAndPlayerCounters(player->index_nr);
13629 player->move_delay_value = original_move_delay_value;
13632 player->is_active = FALSE;
13634 if (player->last_move_dir & MV_HORIZONTAL)
13636 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13637 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13641 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13642 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13645 #if USE_FIXED_BORDER_RUNNING_GFX
13646 if (!moved && !player->is_active)
13648 player->is_moving = FALSE;
13649 player->is_digging = FALSE;
13650 player->is_collecting = FALSE;
13651 player->is_snapping = FALSE;
13652 player->is_pushing = FALSE;
13660 if (moved & MP_MOVING && !ScreenMovPos &&
13661 (player->index_nr == game.centered_player_nr ||
13662 game.centered_player_nr == -1))
13664 if (moved & MP_MOVING && !ScreenMovPos &&
13665 (player == local_player || !options.network))
13668 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13669 int offset = game.scroll_delay_value;
13671 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13673 /* actual player has left the screen -- scroll in that direction */
13674 if (jx != old_jx) /* player has moved horizontally */
13675 scroll_x += (jx - old_jx);
13676 else /* player has moved vertically */
13677 scroll_y += (jy - old_jy);
13681 if (jx != old_jx) /* player has moved horizontally */
13683 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
13684 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13685 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13687 /* don't scroll over playfield boundaries */
13688 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13689 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13691 /* don't scroll more than one field at a time */
13692 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13694 /* don't scroll against the player's moving direction */
13695 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
13696 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13697 scroll_x = old_scroll_x;
13699 else /* player has moved vertically */
13701 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
13702 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13703 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13705 /* don't scroll over playfield boundaries */
13706 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13707 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13709 /* don't scroll more than one field at a time */
13710 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13712 /* don't scroll against the player's moving direction */
13713 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
13714 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13715 scroll_y = old_scroll_y;
13719 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13722 if (!options.network && game.centered_player_nr == -1 &&
13723 !AllPlayersInVisibleScreen())
13725 scroll_x = old_scroll_x;
13726 scroll_y = old_scroll_y;
13730 if (!options.network && !AllPlayersInVisibleScreen())
13732 scroll_x = old_scroll_x;
13733 scroll_y = old_scroll_y;
13738 ScrollScreen(player, SCROLL_INIT);
13739 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13744 player->StepFrame = 0;
13746 if (moved & MP_MOVING)
13748 if (old_jx != jx && old_jy == jy)
13749 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13750 else if (old_jx == jx && old_jy != jy)
13751 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13753 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
13755 player->last_move_dir = player->MovDir;
13756 player->is_moving = TRUE;
13757 player->is_snapping = FALSE;
13758 player->is_switching = FALSE;
13759 player->is_dropping = FALSE;
13760 player->is_dropping_pressed = FALSE;
13761 player->drop_pressed_delay = 0;
13764 /* should better be called here than above, but this breaks some tapes */
13765 ScrollPlayer(player, SCROLL_INIT);
13770 CheckGravityMovementWhenNotMoving(player);
13772 player->is_moving = FALSE;
13774 /* at this point, the player is allowed to move, but cannot move right now
13775 (e.g. because of something blocking the way) -- ensure that the player
13776 is also allowed to move in the next frame (in old versions before 3.1.1,
13777 the player was forced to wait again for eight frames before next try) */
13779 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13780 player->move_delay = 0; /* allow direct movement in the next frame */
13783 if (player->move_delay == -1) /* not yet initialized by DigField() */
13784 player->move_delay = player->move_delay_value;
13786 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13788 TestIfPlayerTouchesBadThing(jx, jy);
13789 TestIfPlayerTouchesCustomElement(jx, jy);
13792 if (!player->active)
13793 RemovePlayer(player);
13798 void ScrollPlayer(struct PlayerInfo *player, int mode)
13800 int jx = player->jx, jy = player->jy;
13801 int last_jx = player->last_jx, last_jy = player->last_jy;
13802 int move_stepsize = TILEX / player->move_delay_value;
13804 #if USE_NEW_PLAYER_SPEED
13805 if (!player->active)
13808 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
13811 if (!player->active || player->MovPos == 0)
13815 if (mode == SCROLL_INIT)
13817 player->actual_frame_counter = FrameCounter;
13818 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13820 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13821 Feld[last_jx][last_jy] == EL_EMPTY)
13823 int last_field_block_delay = 0; /* start with no blocking at all */
13824 int block_delay_adjustment = player->block_delay_adjustment;
13826 /* if player blocks last field, add delay for exactly one move */
13827 if (player->block_last_field)
13829 last_field_block_delay += player->move_delay_value;
13831 /* when blocking enabled, prevent moving up despite gravity */
13832 #if USE_PLAYER_GRAVITY
13833 if (player->gravity && player->MovDir == MV_UP)
13834 block_delay_adjustment = -1;
13836 if (game.gravity && player->MovDir == MV_UP)
13837 block_delay_adjustment = -1;
13841 /* add block delay adjustment (also possible when not blocking) */
13842 last_field_block_delay += block_delay_adjustment;
13844 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13845 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13848 #if USE_NEW_PLAYER_SPEED
13849 if (player->MovPos != 0) /* player has not yet reached destination */
13855 else if (!FrameReached(&player->actual_frame_counter, 1))
13858 #if USE_NEW_PLAYER_SPEED
13859 if (player->MovPos != 0)
13861 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13862 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13864 /* before DrawPlayer() to draw correct player graphic for this case */
13865 if (player->MovPos == 0)
13866 CheckGravityMovement(player);
13869 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13870 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13872 /* before DrawPlayer() to draw correct player graphic for this case */
13873 if (player->MovPos == 0)
13874 CheckGravityMovement(player);
13877 if (player->MovPos == 0) /* player reached destination field */
13879 if (player->move_delay_reset_counter > 0)
13881 player->move_delay_reset_counter--;
13883 if (player->move_delay_reset_counter == 0)
13885 /* continue with normal speed after quickly moving through gate */
13886 HALVE_PLAYER_SPEED(player);
13888 /* be able to make the next move without delay */
13889 player->move_delay = 0;
13893 player->last_jx = jx;
13894 player->last_jy = jy;
13896 if (Feld[jx][jy] == EL_EXIT_OPEN ||
13897 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13899 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
13901 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13902 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13904 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13906 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13907 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
13909 DrawPlayer(player); /* needed here only to cleanup last field */
13910 RemovePlayer(player);
13912 if (local_player->friends_still_needed == 0 ||
13913 IS_SP_ELEMENT(Feld[jx][jy]))
13914 PlayerWins(player);
13917 /* this breaks one level: "machine", level 000 */
13919 int move_direction = player->MovDir;
13920 int enter_side = MV_DIR_OPPOSITE(move_direction);
13921 int leave_side = move_direction;
13922 int old_jx = last_jx;
13923 int old_jy = last_jy;
13924 int old_element = Feld[old_jx][old_jy];
13925 int new_element = Feld[jx][jy];
13927 if (IS_CUSTOM_ELEMENT(old_element))
13928 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13930 player->index_bit, leave_side);
13932 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13933 CE_PLAYER_LEAVES_X,
13934 player->index_bit, leave_side);
13936 if (IS_CUSTOM_ELEMENT(new_element))
13937 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13938 player->index_bit, enter_side);
13940 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13941 CE_PLAYER_ENTERS_X,
13942 player->index_bit, enter_side);
13944 #if USE_FIX_CE_ACTION_WITH_PLAYER
13945 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13946 CE_MOVE_OF_X, move_direction);
13948 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13949 CE_MOVE_OF_X, move_direction);
13953 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13955 TestIfPlayerTouchesBadThing(jx, jy);
13956 TestIfPlayerTouchesCustomElement(jx, jy);
13958 /* needed because pushed element has not yet reached its destination,
13959 so it would trigger a change event at its previous field location */
13960 if (!player->is_pushing)
13961 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
13963 if (!player->active)
13964 RemovePlayer(player);
13967 if (!local_player->LevelSolved && level.use_step_counter)
13977 if (TimeLeft <= 10 && setup.time_limit)
13978 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13981 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13983 DisplayGameControlValues();
13985 DrawGameValue_Time(TimeLeft);
13988 if (!TimeLeft && setup.time_limit)
13989 for (i = 0; i < MAX_PLAYERS; i++)
13990 KillPlayer(&stored_player[i]);
13993 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13995 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13997 DisplayGameControlValues();
14000 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
14001 DrawGameValue_Time(TimePlayed);
14005 if (tape.single_step && tape.recording && !tape.pausing &&
14006 !player->programmed_action)
14007 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
14011 void ScrollScreen(struct PlayerInfo *player, int mode)
14013 static unsigned long screen_frame_counter = 0;
14015 if (mode == SCROLL_INIT)
14017 /* set scrolling step size according to actual player's moving speed */
14018 ScrollStepSize = TILEX / player->move_delay_value;
14020 screen_frame_counter = FrameCounter;
14021 ScreenMovDir = player->MovDir;
14022 ScreenMovPos = player->MovPos;
14023 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14026 else if (!FrameReached(&screen_frame_counter, 1))
14031 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
14032 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14033 redraw_mask |= REDRAW_FIELD;
14036 ScreenMovDir = MV_NONE;
14039 void TestIfPlayerTouchesCustomElement(int x, int y)
14041 static int xy[4][2] =
14048 static int trigger_sides[4][2] =
14050 /* center side border side */
14051 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14052 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14053 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14054 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14056 static int touch_dir[4] =
14058 MV_LEFT | MV_RIGHT,
14063 int center_element = Feld[x][y]; /* should always be non-moving! */
14066 for (i = 0; i < NUM_DIRECTIONS; i++)
14068 int xx = x + xy[i][0];
14069 int yy = y + xy[i][1];
14070 int center_side = trigger_sides[i][0];
14071 int border_side = trigger_sides[i][1];
14072 int border_element;
14074 if (!IN_LEV_FIELD(xx, yy))
14077 if (IS_PLAYER(x, y)) /* player found at center element */
14079 struct PlayerInfo *player = PLAYERINFO(x, y);
14081 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14082 border_element = Feld[xx][yy]; /* may be moving! */
14083 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14084 border_element = Feld[xx][yy];
14085 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14086 border_element = MovingOrBlocked2Element(xx, yy);
14088 continue; /* center and border element do not touch */
14090 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
14091 player->index_bit, border_side);
14092 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
14093 CE_PLAYER_TOUCHES_X,
14094 player->index_bit, border_side);
14096 #if USE_FIX_CE_ACTION_WITH_PLAYER
14098 /* use player element that is initially defined in the level playfield,
14099 not the player element that corresponds to the runtime player number
14100 (example: a level that contains EL_PLAYER_3 as the only player would
14101 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14102 int player_element = PLAYERINFO(x, y)->initial_element;
14104 CheckElementChangeBySide(xx, yy, border_element, player_element,
14105 CE_TOUCHING_X, border_side);
14109 else if (IS_PLAYER(xx, yy)) /* player found at border element */
14111 struct PlayerInfo *player = PLAYERINFO(xx, yy);
14113 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14115 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14116 continue; /* center and border element do not touch */
14119 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14120 player->index_bit, center_side);
14121 CheckTriggeredElementChangeByPlayer(x, y, center_element,
14122 CE_PLAYER_TOUCHES_X,
14123 player->index_bit, center_side);
14125 #if USE_FIX_CE_ACTION_WITH_PLAYER
14127 /* use player element that is initially defined in the level playfield,
14128 not the player element that corresponds to the runtime player number
14129 (example: a level that contains EL_PLAYER_3 as the only player would
14130 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14131 int player_element = PLAYERINFO(xx, yy)->initial_element;
14133 CheckElementChangeBySide(x, y, center_element, player_element,
14134 CE_TOUCHING_X, center_side);
14143 #if USE_ELEMENT_TOUCHING_BUGFIX
14145 void TestIfElementTouchesCustomElement(int x, int y)
14147 static int xy[4][2] =
14154 static int trigger_sides[4][2] =
14156 /* center side border side */
14157 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14158 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14159 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14160 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14162 static int touch_dir[4] =
14164 MV_LEFT | MV_RIGHT,
14169 boolean change_center_element = FALSE;
14170 int center_element = Feld[x][y]; /* should always be non-moving! */
14171 int border_element_old[NUM_DIRECTIONS];
14174 for (i = 0; i < NUM_DIRECTIONS; i++)
14176 int xx = x + xy[i][0];
14177 int yy = y + xy[i][1];
14178 int border_element;
14180 border_element_old[i] = -1;
14182 if (!IN_LEV_FIELD(xx, yy))
14185 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14186 border_element = Feld[xx][yy]; /* may be moving! */
14187 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14188 border_element = Feld[xx][yy];
14189 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14190 border_element = MovingOrBlocked2Element(xx, yy);
14192 continue; /* center and border element do not touch */
14194 border_element_old[i] = border_element;
14197 for (i = 0; i < NUM_DIRECTIONS; i++)
14199 int xx = x + xy[i][0];
14200 int yy = y + xy[i][1];
14201 int center_side = trigger_sides[i][0];
14202 int border_element = border_element_old[i];
14204 if (border_element == -1)
14207 /* check for change of border element */
14208 CheckElementChangeBySide(xx, yy, border_element, center_element,
14209 CE_TOUCHING_X, center_side);
14211 /* (center element cannot be player, so we dont have to check this here) */
14214 for (i = 0; i < NUM_DIRECTIONS; i++)
14216 int xx = x + xy[i][0];
14217 int yy = y + xy[i][1];
14218 int border_side = trigger_sides[i][1];
14219 int border_element = border_element_old[i];
14221 if (border_element == -1)
14224 /* check for change of center element (but change it only once) */
14225 if (!change_center_element)
14226 change_center_element =
14227 CheckElementChangeBySide(x, y, center_element, border_element,
14228 CE_TOUCHING_X, border_side);
14230 #if USE_FIX_CE_ACTION_WITH_PLAYER
14231 if (IS_PLAYER(xx, yy))
14233 /* use player element that is initially defined in the level playfield,
14234 not the player element that corresponds to the runtime player number
14235 (example: a level that contains EL_PLAYER_3 as the only player would
14236 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14237 int player_element = PLAYERINFO(xx, yy)->initial_element;
14239 CheckElementChangeBySide(x, y, center_element, player_element,
14240 CE_TOUCHING_X, border_side);
14248 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14250 static int xy[4][2] =
14257 static int trigger_sides[4][2] =
14259 /* center side border side */
14260 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14261 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14262 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14263 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14265 static int touch_dir[4] =
14267 MV_LEFT | MV_RIGHT,
14272 boolean change_center_element = FALSE;
14273 int center_element = Feld[x][y]; /* should always be non-moving! */
14276 for (i = 0; i < NUM_DIRECTIONS; i++)
14278 int xx = x + xy[i][0];
14279 int yy = y + xy[i][1];
14280 int center_side = trigger_sides[i][0];
14281 int border_side = trigger_sides[i][1];
14282 int border_element;
14284 if (!IN_LEV_FIELD(xx, yy))
14287 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14288 border_element = Feld[xx][yy]; /* may be moving! */
14289 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14290 border_element = Feld[xx][yy];
14291 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14292 border_element = MovingOrBlocked2Element(xx, yy);
14294 continue; /* center and border element do not touch */
14296 /* check for change of center element (but change it only once) */
14297 if (!change_center_element)
14298 change_center_element =
14299 CheckElementChangeBySide(x, y, center_element, border_element,
14300 CE_TOUCHING_X, border_side);
14302 /* check for change of border element */
14303 CheckElementChangeBySide(xx, yy, border_element, center_element,
14304 CE_TOUCHING_X, center_side);
14310 void TestIfElementHitsCustomElement(int x, int y, int direction)
14312 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14313 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14314 int hitx = x + dx, hity = y + dy;
14315 int hitting_element = Feld[x][y];
14316 int touched_element;
14318 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14321 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14322 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14324 if (IN_LEV_FIELD(hitx, hity))
14326 int opposite_direction = MV_DIR_OPPOSITE(direction);
14327 int hitting_side = direction;
14328 int touched_side = opposite_direction;
14329 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14330 MovDir[hitx][hity] != direction ||
14331 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14337 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14338 CE_HITTING_X, touched_side);
14340 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14341 CE_HIT_BY_X, hitting_side);
14343 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14344 CE_HIT_BY_SOMETHING, opposite_direction);
14346 #if USE_FIX_CE_ACTION_WITH_PLAYER
14347 if (IS_PLAYER(hitx, hity))
14349 /* use player element that is initially defined in the level playfield,
14350 not the player element that corresponds to the runtime player number
14351 (example: a level that contains EL_PLAYER_3 as the only player would
14352 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14353 int player_element = PLAYERINFO(hitx, hity)->initial_element;
14355 CheckElementChangeBySide(x, y, hitting_element, player_element,
14356 CE_HITTING_X, touched_side);
14362 /* "hitting something" is also true when hitting the playfield border */
14363 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14364 CE_HITTING_SOMETHING, direction);
14368 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14370 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14371 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14372 int hitx = x + dx, hity = y + dy;
14373 int hitting_element = Feld[x][y];
14374 int touched_element;
14376 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14377 !IS_FREE(hitx, hity) &&
14378 (!IS_MOVING(hitx, hity) ||
14379 MovDir[hitx][hity] != direction ||
14380 ABS(MovPos[hitx][hity]) <= TILEY / 2));
14383 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14387 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14391 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14392 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14394 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14395 EP_CAN_SMASH_EVERYTHING, direction);
14397 if (IN_LEV_FIELD(hitx, hity))
14399 int opposite_direction = MV_DIR_OPPOSITE(direction);
14400 int hitting_side = direction;
14401 int touched_side = opposite_direction;
14403 int touched_element = MovingOrBlocked2Element(hitx, hity);
14406 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14407 MovDir[hitx][hity] != direction ||
14408 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14417 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14418 CE_SMASHED_BY_SOMETHING, opposite_direction);
14420 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14421 CE_OTHER_IS_SMASHING, touched_side);
14423 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14424 CE_OTHER_GETS_SMASHED, hitting_side);
14430 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14432 int i, kill_x = -1, kill_y = -1;
14434 int bad_element = -1;
14435 static int test_xy[4][2] =
14442 static int test_dir[4] =
14450 for (i = 0; i < NUM_DIRECTIONS; i++)
14452 int test_x, test_y, test_move_dir, test_element;
14454 test_x = good_x + test_xy[i][0];
14455 test_y = good_y + test_xy[i][1];
14457 if (!IN_LEV_FIELD(test_x, test_y))
14461 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14463 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14465 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14466 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14468 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14469 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
14473 bad_element = test_element;
14479 if (kill_x != -1 || kill_y != -1)
14481 if (IS_PLAYER(good_x, good_y))
14483 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14485 if (player->shield_deadly_time_left > 0 &&
14486 !IS_INDESTRUCTIBLE(bad_element))
14487 Bang(kill_x, kill_y);
14488 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14489 KillPlayer(player);
14492 Bang(good_x, good_y);
14496 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14498 int i, kill_x = -1, kill_y = -1;
14499 int bad_element = Feld[bad_x][bad_y];
14500 static int test_xy[4][2] =
14507 static int touch_dir[4] =
14509 MV_LEFT | MV_RIGHT,
14514 static int test_dir[4] =
14522 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
14525 for (i = 0; i < NUM_DIRECTIONS; i++)
14527 int test_x, test_y, test_move_dir, test_element;
14529 test_x = bad_x + test_xy[i][0];
14530 test_y = bad_y + test_xy[i][1];
14532 if (!IN_LEV_FIELD(test_x, test_y))
14536 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14538 test_element = Feld[test_x][test_y];
14540 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14541 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14543 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
14544 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
14546 /* good thing is player or penguin that does not move away */
14547 if (IS_PLAYER(test_x, test_y))
14549 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14551 if (bad_element == EL_ROBOT && player->is_moving)
14552 continue; /* robot does not kill player if he is moving */
14554 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14556 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14557 continue; /* center and border element do not touch */
14565 else if (test_element == EL_PENGUIN)
14575 if (kill_x != -1 || kill_y != -1)
14577 if (IS_PLAYER(kill_x, kill_y))
14579 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14581 if (player->shield_deadly_time_left > 0 &&
14582 !IS_INDESTRUCTIBLE(bad_element))
14583 Bang(bad_x, bad_y);
14584 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14585 KillPlayer(player);
14588 Bang(kill_x, kill_y);
14592 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14594 int bad_element = Feld[bad_x][bad_y];
14595 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14596 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
14597 int test_x = bad_x + dx, test_y = bad_y + dy;
14598 int test_move_dir, test_element;
14599 int kill_x = -1, kill_y = -1;
14601 if (!IN_LEV_FIELD(test_x, test_y))
14605 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14607 test_element = Feld[test_x][test_y];
14609 if (test_move_dir != bad_move_dir)
14611 /* good thing can be player or penguin that does not move away */
14612 if (IS_PLAYER(test_x, test_y))
14614 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14616 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14617 player as being hit when he is moving towards the bad thing, because
14618 the "get hit by" condition would be lost after the player stops) */
14619 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14620 return; /* player moves away from bad thing */
14625 else if (test_element == EL_PENGUIN)
14632 if (kill_x != -1 || kill_y != -1)
14634 if (IS_PLAYER(kill_x, kill_y))
14636 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14638 if (player->shield_deadly_time_left > 0 &&
14639 !IS_INDESTRUCTIBLE(bad_element))
14640 Bang(bad_x, bad_y);
14641 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14642 KillPlayer(player);
14645 Bang(kill_x, kill_y);
14649 void TestIfPlayerTouchesBadThing(int x, int y)
14651 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14654 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14656 TestIfGoodThingHitsBadThing(x, y, move_dir);
14659 void TestIfBadThingTouchesPlayer(int x, int y)
14661 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14664 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14666 TestIfBadThingHitsGoodThing(x, y, move_dir);
14669 void TestIfFriendTouchesBadThing(int x, int y)
14671 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14674 void TestIfBadThingTouchesFriend(int x, int y)
14676 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14679 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14681 int i, kill_x = bad_x, kill_y = bad_y;
14682 static int xy[4][2] =
14690 for (i = 0; i < NUM_DIRECTIONS; i++)
14694 x = bad_x + xy[i][0];
14695 y = bad_y + xy[i][1];
14696 if (!IN_LEV_FIELD(x, y))
14699 element = Feld[x][y];
14700 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14701 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14709 if (kill_x != bad_x || kill_y != bad_y)
14710 Bang(bad_x, bad_y);
14713 void KillPlayer(struct PlayerInfo *player)
14715 int jx = player->jx, jy = player->jy;
14717 if (!player->active)
14721 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14722 player->killed, player->active, player->reanimated);
14725 /* the following code was introduced to prevent an infinite loop when calling
14727 -> CheckTriggeredElementChangeExt()
14728 -> ExecuteCustomElementAction()
14730 -> (infinitely repeating the above sequence of function calls)
14731 which occurs when killing the player while having a CE with the setting
14732 "kill player X when explosion of <player X>"; the solution using a new
14733 field "player->killed" was chosen for backwards compatibility, although
14734 clever use of the fields "player->active" etc. would probably also work */
14736 if (player->killed)
14740 player->killed = TRUE;
14742 /* remove accessible field at the player's position */
14743 Feld[jx][jy] = EL_EMPTY;
14745 /* deactivate shield (else Bang()/Explode() would not work right) */
14746 player->shield_normal_time_left = 0;
14747 player->shield_deadly_time_left = 0;
14750 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14751 player->killed, player->active, player->reanimated);
14757 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14758 player->killed, player->active, player->reanimated);
14761 #if USE_PLAYER_REANIMATION
14763 if (player->reanimated) /* killed player may have been reanimated */
14764 player->killed = player->reanimated = FALSE;
14766 BuryPlayer(player);
14768 if (player->killed) /* player may have been reanimated */
14769 BuryPlayer(player);
14772 BuryPlayer(player);
14776 static void KillPlayerUnlessEnemyProtected(int x, int y)
14778 if (!PLAYER_ENEMY_PROTECTED(x, y))
14779 KillPlayer(PLAYERINFO(x, y));
14782 static void KillPlayerUnlessExplosionProtected(int x, int y)
14784 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14785 KillPlayer(PLAYERINFO(x, y));
14788 void BuryPlayer(struct PlayerInfo *player)
14790 int jx = player->jx, jy = player->jy;
14792 if (!player->active)
14795 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14796 PlayLevelSound(jx, jy, SND_GAME_LOSING);
14798 player->GameOver = TRUE;
14799 RemovePlayer(player);
14802 void RemovePlayer(struct PlayerInfo *player)
14804 int jx = player->jx, jy = player->jy;
14805 int i, found = FALSE;
14807 player->present = FALSE;
14808 player->active = FALSE;
14810 if (!ExplodeField[jx][jy])
14811 StorePlayer[jx][jy] = 0;
14813 if (player->is_moving)
14814 TEST_DrawLevelField(player->last_jx, player->last_jy);
14816 for (i = 0; i < MAX_PLAYERS; i++)
14817 if (stored_player[i].active)
14821 AllPlayersGone = TRUE;
14827 #if USE_NEW_SNAP_DELAY
14828 static void setFieldForSnapping(int x, int y, int element, int direction)
14830 struct ElementInfo *ei = &element_info[element];
14831 int direction_bit = MV_DIR_TO_BIT(direction);
14832 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14833 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14834 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14836 Feld[x][y] = EL_ELEMENT_SNAPPING;
14837 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14839 ResetGfxAnimation(x, y);
14841 GfxElement[x][y] = element;
14842 GfxAction[x][y] = action;
14843 GfxDir[x][y] = direction;
14844 GfxFrame[x][y] = -1;
14849 =============================================================================
14850 checkDiagonalPushing()
14851 -----------------------------------------------------------------------------
14852 check if diagonal input device direction results in pushing of object
14853 (by checking if the alternative direction is walkable, diggable, ...)
14854 =============================================================================
14857 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14858 int x, int y, int real_dx, int real_dy)
14860 int jx, jy, dx, dy, xx, yy;
14862 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
14865 /* diagonal direction: check alternative direction */
14870 xx = jx + (dx == 0 ? real_dx : 0);
14871 yy = jy + (dy == 0 ? real_dy : 0);
14873 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14877 =============================================================================
14879 -----------------------------------------------------------------------------
14880 x, y: field next to player (non-diagonal) to try to dig to
14881 real_dx, real_dy: direction as read from input device (can be diagonal)
14882 =============================================================================
14885 static int DigField(struct PlayerInfo *player,
14886 int oldx, int oldy, int x, int y,
14887 int real_dx, int real_dy, int mode)
14889 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14890 boolean player_was_pushing = player->is_pushing;
14891 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14892 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14893 int jx = oldx, jy = oldy;
14894 int dx = x - jx, dy = y - jy;
14895 int nextx = x + dx, nexty = y + dy;
14896 int move_direction = (dx == -1 ? MV_LEFT :
14897 dx == +1 ? MV_RIGHT :
14899 dy == +1 ? MV_DOWN : MV_NONE);
14900 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14901 int dig_side = MV_DIR_OPPOSITE(move_direction);
14902 int old_element = Feld[jx][jy];
14903 #if USE_FIXED_DONT_RUN_INTO
14904 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14910 if (is_player) /* function can also be called by EL_PENGUIN */
14912 if (player->MovPos == 0)
14914 player->is_digging = FALSE;
14915 player->is_collecting = FALSE;
14918 if (player->MovPos == 0) /* last pushing move finished */
14919 player->is_pushing = FALSE;
14921 if (mode == DF_NO_PUSH) /* player just stopped pushing */
14923 player->is_switching = FALSE;
14924 player->push_delay = -1;
14926 return MP_NO_ACTION;
14930 #if !USE_FIXED_DONT_RUN_INTO
14931 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14932 return MP_NO_ACTION;
14935 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14936 old_element = Back[jx][jy];
14938 /* in case of element dropped at player position, check background */
14939 else if (Back[jx][jy] != EL_EMPTY &&
14940 game.engine_version >= VERSION_IDENT(2,2,0,0))
14941 old_element = Back[jx][jy];
14943 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14944 return MP_NO_ACTION; /* field has no opening in this direction */
14946 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14947 return MP_NO_ACTION; /* field has no opening in this direction */
14949 #if USE_FIXED_DONT_RUN_INTO
14950 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14954 Feld[jx][jy] = player->artwork_element;
14955 InitMovingField(jx, jy, MV_DOWN);
14956 Store[jx][jy] = EL_ACID;
14957 ContinueMoving(jx, jy);
14958 BuryPlayer(player);
14960 return MP_DONT_RUN_INTO;
14964 #if USE_FIXED_DONT_RUN_INTO
14965 if (player_can_move && DONT_RUN_INTO(element))
14967 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14969 return MP_DONT_RUN_INTO;
14973 #if USE_FIXED_DONT_RUN_INTO
14974 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14975 return MP_NO_ACTION;
14978 #if !USE_FIXED_DONT_RUN_INTO
14979 element = Feld[x][y];
14982 collect_count = element_info[element].collect_count_initial;
14984 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
14985 return MP_NO_ACTION;
14987 if (game.engine_version < VERSION_IDENT(2,2,0,0))
14988 player_can_move = player_can_move_or_snap;
14990 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14991 game.engine_version >= VERSION_IDENT(2,2,0,0))
14993 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14994 player->index_bit, dig_side);
14995 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14996 player->index_bit, dig_side);
14998 if (element == EL_DC_LANDMINE)
15001 if (Feld[x][y] != element) /* field changed by snapping */
15004 return MP_NO_ACTION;
15007 #if USE_PLAYER_GRAVITY
15008 if (player->gravity && is_player && !player->is_auto_moving &&
15009 canFallDown(player) && move_direction != MV_DOWN &&
15010 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15011 return MP_NO_ACTION; /* player cannot walk here due to gravity */
15013 if (game.gravity && is_player && !player->is_auto_moving &&
15014 canFallDown(player) && move_direction != MV_DOWN &&
15015 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15016 return MP_NO_ACTION; /* player cannot walk here due to gravity */
15019 if (player_can_move &&
15020 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
15022 int sound_element = SND_ELEMENT(element);
15023 int sound_action = ACTION_WALKING;
15025 if (IS_RND_GATE(element))
15027 if (!player->key[RND_GATE_NR(element)])
15028 return MP_NO_ACTION;
15030 else if (IS_RND_GATE_GRAY(element))
15032 if (!player->key[RND_GATE_GRAY_NR(element)])
15033 return MP_NO_ACTION;
15035 else if (IS_RND_GATE_GRAY_ACTIVE(element))
15037 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
15038 return MP_NO_ACTION;
15040 else if (element == EL_EXIT_OPEN ||
15041 element == EL_EM_EXIT_OPEN ||
15043 element == EL_EM_EXIT_OPENING ||
15045 element == EL_STEEL_EXIT_OPEN ||
15046 element == EL_EM_STEEL_EXIT_OPEN ||
15048 element == EL_EM_STEEL_EXIT_OPENING ||
15050 element == EL_SP_EXIT_OPEN ||
15051 element == EL_SP_EXIT_OPENING)
15053 sound_action = ACTION_PASSING; /* player is passing exit */
15055 else if (element == EL_EMPTY)
15057 sound_action = ACTION_MOVING; /* nothing to walk on */
15060 /* play sound from background or player, whatever is available */
15061 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
15062 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
15064 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
15066 else if (player_can_move &&
15067 IS_PASSABLE(element) && canPassField(x, y, move_direction))
15069 if (!ACCESS_FROM(element, opposite_direction))
15070 return MP_NO_ACTION; /* field not accessible from this direction */
15072 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
15073 return MP_NO_ACTION;
15075 if (IS_EM_GATE(element))
15077 if (!player->key[EM_GATE_NR(element)])
15078 return MP_NO_ACTION;
15080 else if (IS_EM_GATE_GRAY(element))
15082 if (!player->key[EM_GATE_GRAY_NR(element)])
15083 return MP_NO_ACTION;
15085 else if (IS_EM_GATE_GRAY_ACTIVE(element))
15087 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
15088 return MP_NO_ACTION;
15090 else if (IS_EMC_GATE(element))
15092 if (!player->key[EMC_GATE_NR(element)])
15093 return MP_NO_ACTION;
15095 else if (IS_EMC_GATE_GRAY(element))
15097 if (!player->key[EMC_GATE_GRAY_NR(element)])
15098 return MP_NO_ACTION;
15100 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
15102 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
15103 return MP_NO_ACTION;
15105 else if (element == EL_DC_GATE_WHITE ||
15106 element == EL_DC_GATE_WHITE_GRAY ||
15107 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15109 if (player->num_white_keys == 0)
15110 return MP_NO_ACTION;
15112 player->num_white_keys--;
15114 else if (IS_SP_PORT(element))
15116 if (element == EL_SP_GRAVITY_PORT_LEFT ||
15117 element == EL_SP_GRAVITY_PORT_RIGHT ||
15118 element == EL_SP_GRAVITY_PORT_UP ||
15119 element == EL_SP_GRAVITY_PORT_DOWN)
15120 #if USE_PLAYER_GRAVITY
15121 player->gravity = !player->gravity;
15123 game.gravity = !game.gravity;
15125 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15126 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15127 element == EL_SP_GRAVITY_ON_PORT_UP ||
15128 element == EL_SP_GRAVITY_ON_PORT_DOWN)
15129 #if USE_PLAYER_GRAVITY
15130 player->gravity = TRUE;
15132 game.gravity = TRUE;
15134 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15135 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15136 element == EL_SP_GRAVITY_OFF_PORT_UP ||
15137 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15138 #if USE_PLAYER_GRAVITY
15139 player->gravity = FALSE;
15141 game.gravity = FALSE;
15145 /* automatically move to the next field with double speed */
15146 player->programmed_action = move_direction;
15148 if (player->move_delay_reset_counter == 0)
15150 player->move_delay_reset_counter = 2; /* two double speed steps */
15152 DOUBLE_PLAYER_SPEED(player);
15155 PlayLevelSoundAction(x, y, ACTION_PASSING);
15157 else if (player_can_move_or_snap && IS_DIGGABLE(element))
15161 if (mode != DF_SNAP)
15163 GfxElement[x][y] = GFX_ELEMENT(element);
15164 player->is_digging = TRUE;
15167 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15169 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15170 player->index_bit, dig_side);
15172 if (mode == DF_SNAP)
15174 #if USE_NEW_SNAP_DELAY
15175 if (level.block_snap_field)
15176 setFieldForSnapping(x, y, element, move_direction);
15178 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15180 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15183 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15184 player->index_bit, dig_side);
15187 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15191 if (is_player && mode != DF_SNAP)
15193 GfxElement[x][y] = element;
15194 player->is_collecting = TRUE;
15197 if (element == EL_SPEED_PILL)
15199 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15201 else if (element == EL_EXTRA_TIME && level.time > 0)
15203 TimeLeft += level.extra_time;
15206 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15208 DisplayGameControlValues();
15210 DrawGameValue_Time(TimeLeft);
15213 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15215 player->shield_normal_time_left += level.shield_normal_time;
15216 if (element == EL_SHIELD_DEADLY)
15217 player->shield_deadly_time_left += level.shield_deadly_time;
15219 else if (element == EL_DYNAMITE ||
15220 element == EL_EM_DYNAMITE ||
15221 element == EL_SP_DISK_RED)
15223 if (player->inventory_size < MAX_INVENTORY_SIZE)
15224 player->inventory_element[player->inventory_size++] = element;
15226 DrawGameDoorValues();
15228 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15230 player->dynabomb_count++;
15231 player->dynabombs_left++;
15233 else if (element == EL_DYNABOMB_INCREASE_SIZE)
15235 player->dynabomb_size++;
15237 else if (element == EL_DYNABOMB_INCREASE_POWER)
15239 player->dynabomb_xl = TRUE;
15241 else if (IS_KEY(element))
15243 player->key[KEY_NR(element)] = TRUE;
15245 DrawGameDoorValues();
15247 else if (element == EL_DC_KEY_WHITE)
15249 player->num_white_keys++;
15251 /* display white keys? */
15252 /* DrawGameDoorValues(); */
15254 else if (IS_ENVELOPE(element))
15256 player->show_envelope = element;
15258 else if (element == EL_EMC_LENSES)
15260 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15262 RedrawAllInvisibleElementsForLenses();
15264 else if (element == EL_EMC_MAGNIFIER)
15266 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15268 RedrawAllInvisibleElementsForMagnifier();
15270 else if (IS_DROPPABLE(element) ||
15271 IS_THROWABLE(element)) /* can be collected and dropped */
15275 if (collect_count == 0)
15276 player->inventory_infinite_element = element;
15278 for (i = 0; i < collect_count; i++)
15279 if (player->inventory_size < MAX_INVENTORY_SIZE)
15280 player->inventory_element[player->inventory_size++] = element;
15282 DrawGameDoorValues();
15284 else if (collect_count > 0)
15286 local_player->gems_still_needed -= collect_count;
15287 if (local_player->gems_still_needed < 0)
15288 local_player->gems_still_needed = 0;
15291 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15293 DisplayGameControlValues();
15295 DrawGameValue_Emeralds(local_player->gems_still_needed);
15299 RaiseScoreElement(element);
15300 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15303 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15304 player->index_bit, dig_side);
15306 if (mode == DF_SNAP)
15308 #if USE_NEW_SNAP_DELAY
15309 if (level.block_snap_field)
15310 setFieldForSnapping(x, y, element, move_direction);
15312 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15314 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15317 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15318 player->index_bit, dig_side);
15321 else if (player_can_move_or_snap && IS_PUSHABLE(element))
15323 if (mode == DF_SNAP && element != EL_BD_ROCK)
15324 return MP_NO_ACTION;
15326 if (CAN_FALL(element) && dy)
15327 return MP_NO_ACTION;
15329 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15330 !(element == EL_SPRING && level.use_spring_bug))
15331 return MP_NO_ACTION;
15333 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15334 ((move_direction & MV_VERTICAL &&
15335 ((element_info[element].move_pattern & MV_LEFT &&
15336 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15337 (element_info[element].move_pattern & MV_RIGHT &&
15338 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15339 (move_direction & MV_HORIZONTAL &&
15340 ((element_info[element].move_pattern & MV_UP &&
15341 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15342 (element_info[element].move_pattern & MV_DOWN &&
15343 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15344 return MP_NO_ACTION;
15346 /* do not push elements already moving away faster than player */
15347 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15348 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15349 return MP_NO_ACTION;
15351 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15353 if (player->push_delay_value == -1 || !player_was_pushing)
15354 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15356 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15358 if (player->push_delay_value == -1)
15359 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15361 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15363 if (!player->is_pushing)
15364 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15367 player->is_pushing = TRUE;
15368 player->is_active = TRUE;
15370 if (!(IN_LEV_FIELD(nextx, nexty) &&
15371 (IS_FREE(nextx, nexty) ||
15372 (IS_SB_ELEMENT(element) &&
15373 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15374 (IS_CUSTOM_ELEMENT(element) &&
15375 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15376 return MP_NO_ACTION;
15378 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15379 return MP_NO_ACTION;
15381 if (player->push_delay == -1) /* new pushing; restart delay */
15382 player->push_delay = 0;
15384 if (player->push_delay < player->push_delay_value &&
15385 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15386 element != EL_SPRING && element != EL_BALLOON)
15388 /* make sure that there is no move delay before next try to push */
15389 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15390 player->move_delay = 0;
15392 return MP_NO_ACTION;
15395 if (IS_CUSTOM_ELEMENT(element) &&
15396 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15398 if (!DigFieldByCE(nextx, nexty, element))
15399 return MP_NO_ACTION;
15402 if (IS_SB_ELEMENT(element))
15404 if (element == EL_SOKOBAN_FIELD_FULL)
15406 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15407 local_player->sokobanfields_still_needed++;
15410 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15412 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15413 local_player->sokobanfields_still_needed--;
15416 Feld[x][y] = EL_SOKOBAN_OBJECT;
15418 if (Back[x][y] == Back[nextx][nexty])
15419 PlayLevelSoundAction(x, y, ACTION_PUSHING);
15420 else if (Back[x][y] != 0)
15421 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15424 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15428 if (local_player->sokobanfields_still_needed == 0 &&
15429 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
15431 if (local_player->sokobanfields_still_needed == 0 &&
15432 game.emulation == EMU_SOKOBAN)
15435 PlayerWins(player);
15437 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15441 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15443 InitMovingField(x, y, move_direction);
15444 GfxAction[x][y] = ACTION_PUSHING;
15446 if (mode == DF_SNAP)
15447 ContinueMoving(x, y);
15449 MovPos[x][y] = (dx != 0 ? dx : dy);
15451 Pushed[x][y] = TRUE;
15452 Pushed[nextx][nexty] = TRUE;
15454 if (game.engine_version < VERSION_IDENT(2,2,0,7))
15455 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15457 player->push_delay_value = -1; /* get new value later */
15459 /* check for element change _after_ element has been pushed */
15460 if (game.use_change_when_pushing_bug)
15462 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15463 player->index_bit, dig_side);
15464 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15465 player->index_bit, dig_side);
15468 else if (IS_SWITCHABLE(element))
15470 if (PLAYER_SWITCHING(player, x, y))
15472 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15473 player->index_bit, dig_side);
15478 player->is_switching = TRUE;
15479 player->switch_x = x;
15480 player->switch_y = y;
15482 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15484 if (element == EL_ROBOT_WHEEL)
15486 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15490 game.robot_wheel_active = TRUE;
15492 TEST_DrawLevelField(x, y);
15494 else if (element == EL_SP_TERMINAL)
15498 SCAN_PLAYFIELD(xx, yy)
15500 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15502 else if (Feld[xx][yy] == EL_SP_TERMINAL)
15503 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15506 else if (IS_BELT_SWITCH(element))
15508 ToggleBeltSwitch(x, y);
15510 else if (element == EL_SWITCHGATE_SWITCH_UP ||
15511 element == EL_SWITCHGATE_SWITCH_DOWN ||
15512 element == EL_DC_SWITCHGATE_SWITCH_UP ||
15513 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15515 ToggleSwitchgateSwitch(x, y);
15517 else if (element == EL_LIGHT_SWITCH ||
15518 element == EL_LIGHT_SWITCH_ACTIVE)
15520 ToggleLightSwitch(x, y);
15522 else if (element == EL_TIMEGATE_SWITCH ||
15523 element == EL_DC_TIMEGATE_SWITCH)
15525 ActivateTimegateSwitch(x, y);
15527 else if (element == EL_BALLOON_SWITCH_LEFT ||
15528 element == EL_BALLOON_SWITCH_RIGHT ||
15529 element == EL_BALLOON_SWITCH_UP ||
15530 element == EL_BALLOON_SWITCH_DOWN ||
15531 element == EL_BALLOON_SWITCH_NONE ||
15532 element == EL_BALLOON_SWITCH_ANY)
15534 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
15535 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15536 element == EL_BALLOON_SWITCH_UP ? MV_UP :
15537 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
15538 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
15541 else if (element == EL_LAMP)
15543 Feld[x][y] = EL_LAMP_ACTIVE;
15544 local_player->lights_still_needed--;
15546 ResetGfxAnimation(x, y);
15547 TEST_DrawLevelField(x, y);
15549 else if (element == EL_TIME_ORB_FULL)
15551 Feld[x][y] = EL_TIME_ORB_EMPTY;
15553 if (level.time > 0 || level.use_time_orb_bug)
15555 TimeLeft += level.time_orb_time;
15558 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15560 DisplayGameControlValues();
15562 DrawGameValue_Time(TimeLeft);
15566 ResetGfxAnimation(x, y);
15567 TEST_DrawLevelField(x, y);
15569 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15570 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15574 game.ball_state = !game.ball_state;
15576 SCAN_PLAYFIELD(xx, yy)
15578 int e = Feld[xx][yy];
15580 if (game.ball_state)
15582 if (e == EL_EMC_MAGIC_BALL)
15583 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15584 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15585 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15589 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15590 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15591 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15592 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15597 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15598 player->index_bit, dig_side);
15600 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15601 player->index_bit, dig_side);
15603 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15604 player->index_bit, dig_side);
15610 if (!PLAYER_SWITCHING(player, x, y))
15612 player->is_switching = TRUE;
15613 player->switch_x = x;
15614 player->switch_y = y;
15616 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15617 player->index_bit, dig_side);
15618 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15619 player->index_bit, dig_side);
15621 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15622 player->index_bit, dig_side);
15623 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15624 player->index_bit, dig_side);
15627 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15628 player->index_bit, dig_side);
15629 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15630 player->index_bit, dig_side);
15632 return MP_NO_ACTION;
15635 player->push_delay = -1;
15637 if (is_player) /* function can also be called by EL_PENGUIN */
15639 if (Feld[x][y] != element) /* really digged/collected something */
15641 player->is_collecting = !player->is_digging;
15642 player->is_active = TRUE;
15649 static boolean DigFieldByCE(int x, int y, int digging_element)
15651 int element = Feld[x][y];
15653 if (!IS_FREE(x, y))
15655 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15656 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15659 /* no element can dig solid indestructible elements */
15660 if (IS_INDESTRUCTIBLE(element) &&
15661 !IS_DIGGABLE(element) &&
15662 !IS_COLLECTIBLE(element))
15665 if (AmoebaNr[x][y] &&
15666 (element == EL_AMOEBA_FULL ||
15667 element == EL_BD_AMOEBA ||
15668 element == EL_AMOEBA_GROWING))
15670 AmoebaCnt[AmoebaNr[x][y]]--;
15671 AmoebaCnt2[AmoebaNr[x][y]]--;
15674 if (IS_MOVING(x, y))
15675 RemoveMovingField(x, y);
15679 TEST_DrawLevelField(x, y);
15682 /* if digged element was about to explode, prevent the explosion */
15683 ExplodeField[x][y] = EX_TYPE_NONE;
15685 PlayLevelSoundAction(x, y, action);
15688 Store[x][y] = EL_EMPTY;
15691 /* this makes it possible to leave the removed element again */
15692 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15693 Store[x][y] = element;
15695 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15697 int move_leave_element = element_info[digging_element].move_leave_element;
15699 /* this makes it possible to leave the removed element again */
15700 Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15701 element : move_leave_element);
15708 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15710 int jx = player->jx, jy = player->jy;
15711 int x = jx + dx, y = jy + dy;
15712 int snap_direction = (dx == -1 ? MV_LEFT :
15713 dx == +1 ? MV_RIGHT :
15715 dy == +1 ? MV_DOWN : MV_NONE);
15716 boolean can_continue_snapping = (level.continuous_snapping &&
15717 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15719 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15722 if (!player->active || !IN_LEV_FIELD(x, y))
15730 if (player->MovPos == 0)
15731 player->is_pushing = FALSE;
15733 player->is_snapping = FALSE;
15735 if (player->MovPos == 0)
15737 player->is_moving = FALSE;
15738 player->is_digging = FALSE;
15739 player->is_collecting = FALSE;
15745 #if USE_NEW_CONTINUOUS_SNAPPING
15746 /* prevent snapping with already pressed snap key when not allowed */
15747 if (player->is_snapping && !can_continue_snapping)
15750 if (player->is_snapping)
15754 player->MovDir = snap_direction;
15756 if (player->MovPos == 0)
15758 player->is_moving = FALSE;
15759 player->is_digging = FALSE;
15760 player->is_collecting = FALSE;
15763 player->is_dropping = FALSE;
15764 player->is_dropping_pressed = FALSE;
15765 player->drop_pressed_delay = 0;
15767 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15770 player->is_snapping = TRUE;
15771 player->is_active = TRUE;
15773 if (player->MovPos == 0)
15775 player->is_moving = FALSE;
15776 player->is_digging = FALSE;
15777 player->is_collecting = FALSE;
15780 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
15781 TEST_DrawLevelField(player->last_jx, player->last_jy);
15783 TEST_DrawLevelField(x, y);
15788 static boolean DropElement(struct PlayerInfo *player)
15790 int old_element, new_element;
15791 int dropx = player->jx, dropy = player->jy;
15792 int drop_direction = player->MovDir;
15793 int drop_side = drop_direction;
15795 int drop_element = get_next_dropped_element(player);
15797 int drop_element = (player->inventory_size > 0 ?
15798 player->inventory_element[player->inventory_size - 1] :
15799 player->inventory_infinite_element != EL_UNDEFINED ?
15800 player->inventory_infinite_element :
15801 player->dynabombs_left > 0 ?
15802 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15806 player->is_dropping_pressed = TRUE;
15808 /* do not drop an element on top of another element; when holding drop key
15809 pressed without moving, dropped element must move away before the next
15810 element can be dropped (this is especially important if the next element
15811 is dynamite, which can be placed on background for historical reasons) */
15812 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15815 if (IS_THROWABLE(drop_element))
15817 dropx += GET_DX_FROM_DIR(drop_direction);
15818 dropy += GET_DY_FROM_DIR(drop_direction);
15820 if (!IN_LEV_FIELD(dropx, dropy))
15824 old_element = Feld[dropx][dropy]; /* old element at dropping position */
15825 new_element = drop_element; /* default: no change when dropping */
15827 /* check if player is active, not moving and ready to drop */
15828 if (!player->active || player->MovPos || player->drop_delay > 0)
15831 /* check if player has anything that can be dropped */
15832 if (new_element == EL_UNDEFINED)
15835 /* check if drop key was pressed long enough for EM style dynamite */
15836 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15839 /* check if anything can be dropped at the current position */
15840 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15843 /* collected custom elements can only be dropped on empty fields */
15844 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15847 if (old_element != EL_EMPTY)
15848 Back[dropx][dropy] = old_element; /* store old element on this field */
15850 ResetGfxAnimation(dropx, dropy);
15851 ResetRandomAnimationValue(dropx, dropy);
15853 if (player->inventory_size > 0 ||
15854 player->inventory_infinite_element != EL_UNDEFINED)
15856 if (player->inventory_size > 0)
15858 player->inventory_size--;
15860 DrawGameDoorValues();
15862 if (new_element == EL_DYNAMITE)
15863 new_element = EL_DYNAMITE_ACTIVE;
15864 else if (new_element == EL_EM_DYNAMITE)
15865 new_element = EL_EM_DYNAMITE_ACTIVE;
15866 else if (new_element == EL_SP_DISK_RED)
15867 new_element = EL_SP_DISK_RED_ACTIVE;
15870 Feld[dropx][dropy] = new_element;
15872 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15873 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15874 el2img(Feld[dropx][dropy]), 0);
15876 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15878 /* needed if previous element just changed to "empty" in the last frame */
15879 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15881 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15882 player->index_bit, drop_side);
15883 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15885 player->index_bit, drop_side);
15887 TestIfElementTouchesCustomElement(dropx, dropy);
15889 else /* player is dropping a dyna bomb */
15891 player->dynabombs_left--;
15893 Feld[dropx][dropy] = new_element;
15895 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15896 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15897 el2img(Feld[dropx][dropy]), 0);
15899 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15902 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15903 InitField_WithBug1(dropx, dropy, FALSE);
15905 new_element = Feld[dropx][dropy]; /* element might have changed */
15907 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15908 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15910 int move_direction, nextx, nexty;
15912 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15913 MovDir[dropx][dropy] = drop_direction;
15915 move_direction = MovDir[dropx][dropy];
15916 nextx = dropx + GET_DX_FROM_DIR(move_direction);
15917 nexty = dropy + GET_DY_FROM_DIR(move_direction);
15919 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15921 #if USE_FIX_IMPACT_COLLISION
15922 /* do not cause impact style collision by dropping elements that can fall */
15923 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15925 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15929 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15930 player->is_dropping = TRUE;
15932 player->drop_pressed_delay = 0;
15933 player->is_dropping_pressed = FALSE;
15935 player->drop_x = dropx;
15936 player->drop_y = dropy;
15941 /* ------------------------------------------------------------------------- */
15942 /* game sound playing functions */
15943 /* ------------------------------------------------------------------------- */
15945 static int *loop_sound_frame = NULL;
15946 static int *loop_sound_volume = NULL;
15948 void InitPlayLevelSound()
15950 int num_sounds = getSoundListSize();
15952 checked_free(loop_sound_frame);
15953 checked_free(loop_sound_volume);
15955 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
15956 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15959 static void PlayLevelSound(int x, int y, int nr)
15961 int sx = SCREENX(x), sy = SCREENY(y);
15962 int volume, stereo_position;
15963 int max_distance = 8;
15964 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15966 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15967 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15970 if (!IN_LEV_FIELD(x, y) ||
15971 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15972 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15975 volume = SOUND_MAX_VOLUME;
15977 if (!IN_SCR_FIELD(sx, sy))
15979 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15980 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15982 volume -= volume * (dx > dy ? dx : dy) / max_distance;
15985 stereo_position = (SOUND_MAX_LEFT +
15986 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15987 (SCR_FIELDX + 2 * max_distance));
15989 if (IS_LOOP_SOUND(nr))
15991 /* This assures that quieter loop sounds do not overwrite louder ones,
15992 while restarting sound volume comparison with each new game frame. */
15994 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15997 loop_sound_volume[nr] = volume;
15998 loop_sound_frame[nr] = FrameCounter;
16001 PlaySoundExt(nr, volume, stereo_position, type);
16004 static void PlayLevelSoundNearest(int x, int y, int sound_action)
16006 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
16007 x > LEVELX(BX2) ? LEVELX(BX2) : x,
16008 y < LEVELY(BY1) ? LEVELY(BY1) :
16009 y > LEVELY(BY2) ? LEVELY(BY2) : y,
16013 static void PlayLevelSoundAction(int x, int y, int action)
16015 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
16018 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
16020 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16022 if (sound_effect != SND_UNDEFINED)
16023 PlayLevelSound(x, y, sound_effect);
16026 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
16029 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16031 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16032 PlayLevelSound(x, y, sound_effect);
16035 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
16037 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16039 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16040 PlayLevelSound(x, y, sound_effect);
16043 static void StopLevelSoundActionIfLoop(int x, int y, int action)
16045 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16047 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16048 StopSound(sound_effect);
16051 static void PlayLevelMusic()
16053 if (levelset.music[level_nr] != MUS_UNDEFINED)
16054 PlayMusic(levelset.music[level_nr]); /* from config file */
16056 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
16059 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
16061 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
16062 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
16063 int x = xx - 1 - offset;
16064 int y = yy - 1 - offset;
16069 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
16073 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16077 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16081 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16085 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16089 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16093 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16096 case SAMPLE_android_clone:
16097 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16100 case SAMPLE_android_move:
16101 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16104 case SAMPLE_spring:
16105 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16109 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16113 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16116 case SAMPLE_eater_eat:
16117 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16121 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16124 case SAMPLE_collect:
16125 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16128 case SAMPLE_diamond:
16129 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16132 case SAMPLE_squash:
16133 /* !!! CHECK THIS !!! */
16135 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16137 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16141 case SAMPLE_wonderfall:
16142 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16146 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16150 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16154 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16158 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16162 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16166 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16169 case SAMPLE_wonder:
16170 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16174 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16177 case SAMPLE_exit_open:
16178 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16181 case SAMPLE_exit_leave:
16182 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16185 case SAMPLE_dynamite:
16186 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16190 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16194 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16198 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16202 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16206 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16210 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16214 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16219 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
16221 int element = map_element_SP_to_RND(element_sp);
16222 int action = map_action_SP_to_RND(action_sp);
16223 int offset = (setup.sp_show_border_elements ? 0 : 1);
16224 int x = xx - offset;
16225 int y = yy - offset;
16228 printf("::: %d -> %d\n", element_sp, action_sp);
16231 PlayLevelSoundElementAction(x, y, element, action);
16235 void ChangeTime(int value)
16237 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
16241 /* EMC game engine uses value from time counter of RND game engine */
16242 level.native_em_level->lev->time = *time;
16244 DrawGameValue_Time(*time);
16247 void RaiseScore(int value)
16249 /* EMC game engine and RND game engine have separate score counters */
16250 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
16251 &level.native_em_level->lev->score : &local_player->score);
16255 DrawGameValue_Score(*score);
16259 void RaiseScore(int value)
16261 local_player->score += value;
16264 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16266 DisplayGameControlValues();
16268 DrawGameValue_Score(local_player->score);
16272 void RaiseScoreElement(int element)
16277 case EL_BD_DIAMOND:
16278 case EL_EMERALD_YELLOW:
16279 case EL_EMERALD_RED:
16280 case EL_EMERALD_PURPLE:
16281 case EL_SP_INFOTRON:
16282 RaiseScore(level.score[SC_EMERALD]);
16285 RaiseScore(level.score[SC_DIAMOND]);
16288 RaiseScore(level.score[SC_CRYSTAL]);
16291 RaiseScore(level.score[SC_PEARL]);
16294 case EL_BD_BUTTERFLY:
16295 case EL_SP_ELECTRON:
16296 RaiseScore(level.score[SC_BUG]);
16299 case EL_BD_FIREFLY:
16300 case EL_SP_SNIKSNAK:
16301 RaiseScore(level.score[SC_SPACESHIP]);
16304 case EL_DARK_YAMYAM:
16305 RaiseScore(level.score[SC_YAMYAM]);
16308 RaiseScore(level.score[SC_ROBOT]);
16311 RaiseScore(level.score[SC_PACMAN]);
16314 RaiseScore(level.score[SC_NUT]);
16317 case EL_EM_DYNAMITE:
16318 case EL_SP_DISK_RED:
16319 case EL_DYNABOMB_INCREASE_NUMBER:
16320 case EL_DYNABOMB_INCREASE_SIZE:
16321 case EL_DYNABOMB_INCREASE_POWER:
16322 RaiseScore(level.score[SC_DYNAMITE]);
16324 case EL_SHIELD_NORMAL:
16325 case EL_SHIELD_DEADLY:
16326 RaiseScore(level.score[SC_SHIELD]);
16328 case EL_EXTRA_TIME:
16329 RaiseScore(level.extra_time_score);
16343 case EL_DC_KEY_WHITE:
16344 RaiseScore(level.score[SC_KEY]);
16347 RaiseScore(element_info[element].collect_score);
16352 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16354 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16356 #if defined(NETWORK_AVALIABLE)
16357 if (options.network)
16358 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16367 FadeSkipNextFadeIn();
16369 fading = fading_none;
16373 OpenDoor(DOOR_CLOSE_1);
16376 game_status = GAME_MODE_MAIN;
16379 DrawAndFadeInMainMenu(REDRAW_FIELD);
16387 FadeOut(REDRAW_FIELD);
16390 game_status = GAME_MODE_MAIN;
16392 DrawAndFadeInMainMenu(REDRAW_FIELD);
16396 else /* continue playing the game */
16398 if (tape.playing && tape.deactivate_display)
16399 TapeDeactivateDisplayOff(TRUE);
16401 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16403 if (tape.playing && tape.deactivate_display)
16404 TapeDeactivateDisplayOn();
16408 void RequestQuitGame(boolean ask_if_really_quit)
16410 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16411 boolean skip_request = AllPlayersGone || quick_quit;
16413 RequestQuitGameExt(skip_request, quick_quit,
16414 "Do you really want to quit the game ?");
16418 /* ------------------------------------------------------------------------- */
16419 /* random generator functions */
16420 /* ------------------------------------------------------------------------- */
16422 unsigned int InitEngineRandom_RND(long seed)
16424 game.num_random_calls = 0;
16427 unsigned int rnd_seed = InitEngineRandom(seed);
16429 printf("::: START RND: %d\n", rnd_seed);
16434 return InitEngineRandom(seed);
16440 unsigned int RND(int max)
16444 game.num_random_calls++;
16446 return GetEngineRandom(max);
16453 /* ------------------------------------------------------------------------- */
16454 /* game engine snapshot handling functions */
16455 /* ------------------------------------------------------------------------- */
16457 struct EngineSnapshotInfo
16459 /* runtime values for custom element collect score */
16460 int collect_score[NUM_CUSTOM_ELEMENTS];
16462 /* runtime values for group element choice position */
16463 int choice_pos[NUM_GROUP_ELEMENTS];
16465 /* runtime values for belt position animations */
16466 int belt_graphic[4][NUM_BELT_PARTS];
16467 int belt_anim_mode[4][NUM_BELT_PARTS];
16470 static struct EngineSnapshotInfo engine_snapshot_rnd;
16471 static char *snapshot_level_identifier = NULL;
16472 static int snapshot_level_nr = -1;
16474 static void SaveEngineSnapshotValues_RND()
16476 static int belt_base_active_element[4] =
16478 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16479 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16480 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16481 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16485 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16487 int element = EL_CUSTOM_START + i;
16489 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16492 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16494 int element = EL_GROUP_START + i;
16496 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16499 for (i = 0; i < 4; i++)
16501 for (j = 0; j < NUM_BELT_PARTS; j++)
16503 int element = belt_base_active_element[i] + j;
16504 int graphic = el2img(element);
16505 int anim_mode = graphic_info[graphic].anim_mode;
16507 engine_snapshot_rnd.belt_graphic[i][j] = graphic;
16508 engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
16513 static void LoadEngineSnapshotValues_RND()
16515 unsigned long num_random_calls = game.num_random_calls;
16518 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16520 int element = EL_CUSTOM_START + i;
16522 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16525 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16527 int element = EL_GROUP_START + i;
16529 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16532 for (i = 0; i < 4; i++)
16534 for (j = 0; j < NUM_BELT_PARTS; j++)
16536 int graphic = engine_snapshot_rnd.belt_graphic[i][j];
16537 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
16539 graphic_info[graphic].anim_mode = anim_mode;
16543 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16545 InitRND(tape.random_seed);
16546 for (i = 0; i < num_random_calls; i++)
16550 if (game.num_random_calls != num_random_calls)
16552 Error(ERR_INFO, "number of random calls out of sync");
16553 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16554 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16555 Error(ERR_EXIT, "this should not happen -- please debug");
16559 void SaveEngineSnapshot()
16561 /* do not save snapshots from editor */
16562 if (level_editor_test_game)
16565 /* free previous snapshot buffers, if needed */
16566 FreeEngineSnapshotBuffers();
16568 /* copy some special values to a structure better suited for the snapshot */
16570 SaveEngineSnapshotValues_RND();
16571 SaveEngineSnapshotValues_EM();
16572 SaveEngineSnapshotValues_SP();
16574 /* save values stored in special snapshot structure */
16576 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16577 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16578 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16580 /* save further RND engine values */
16582 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16583 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16584 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16586 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16587 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16588 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16589 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16591 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16592 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16593 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16594 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16595 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16597 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16598 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16599 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16601 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16603 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16605 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16606 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16608 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16609 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16610 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16611 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16612 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16613 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16614 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16615 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16616 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16617 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16618 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16619 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16620 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16621 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16622 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16623 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16624 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16625 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16627 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16628 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16630 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16631 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16632 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16634 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16635 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16637 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16638 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16639 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16640 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16641 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16643 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16644 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16646 /* save level identification information */
16648 setString(&snapshot_level_identifier, leveldir_current->identifier);
16649 snapshot_level_nr = level_nr;
16652 ListNode *node = engine_snapshot_list_rnd;
16655 while (node != NULL)
16657 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16662 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16666 void LoadEngineSnapshot()
16668 /* restore generically stored snapshot buffers */
16670 LoadEngineSnapshotBuffers();
16672 /* restore special values from snapshot structure */
16674 LoadEngineSnapshotValues_RND();
16675 LoadEngineSnapshotValues_EM();
16676 LoadEngineSnapshotValues_SP();
16679 boolean CheckEngineSnapshot()
16681 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16682 snapshot_level_nr == level_nr);
16686 /* ---------- new game button stuff ---------------------------------------- */
16688 /* graphic position values for game buttons */
16689 #define GAME_BUTTON_XSIZE 30
16690 #define GAME_BUTTON_YSIZE 30
16691 #define GAME_BUTTON_XPOS 5
16692 #define GAME_BUTTON_YPOS 215
16693 #define SOUND_BUTTON_XPOS 5
16694 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
16696 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16697 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16698 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16699 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16700 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16701 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16709 } gamebutton_info[NUM_GAME_BUTTONS] =
16713 &game.button.stop.x, &game.button.stop.y,
16714 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
16719 &game.button.pause.x, &game.button.pause.y,
16720 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
16721 GAME_CTRL_ID_PAUSE,
16725 &game.button.play.x, &game.button.play.y,
16726 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
16731 &game.button.sound_music.x, &game.button.sound_music.y,
16732 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
16733 SOUND_CTRL_ID_MUSIC,
16734 "background music on/off"
16737 &game.button.sound_loops.x, &game.button.sound_loops.y,
16738 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
16739 SOUND_CTRL_ID_LOOPS,
16740 "sound loops on/off"
16743 &game.button.sound_simple.x,&game.button.sound_simple.y,
16744 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
16745 SOUND_CTRL_ID_SIMPLE,
16746 "normal sounds on/off"
16750 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
16755 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
16756 GAME_CTRL_ID_PAUSE,
16760 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
16765 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
16766 SOUND_CTRL_ID_MUSIC,
16767 "background music on/off"
16770 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
16771 SOUND_CTRL_ID_LOOPS,
16772 "sound loops on/off"
16775 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
16776 SOUND_CTRL_ID_SIMPLE,
16777 "normal sounds on/off"
16782 void CreateGameButtons()
16786 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16788 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
16789 struct GadgetInfo *gi;
16792 unsigned long event_mask;
16794 int gd_xoffset, gd_yoffset;
16795 int gd_x1, gd_x2, gd_y1, gd_y2;
16798 x = DX + *gamebutton_info[i].x;
16799 y = DY + *gamebutton_info[i].y;
16800 gd_xoffset = gamebutton_info[i].gd_x;
16801 gd_yoffset = gamebutton_info[i].gd_y;
16802 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
16803 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
16805 if (id == GAME_CTRL_ID_STOP ||
16806 id == GAME_CTRL_ID_PAUSE ||
16807 id == GAME_CTRL_ID_PLAY)
16809 button_type = GD_TYPE_NORMAL_BUTTON;
16811 event_mask = GD_EVENT_RELEASED;
16812 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16813 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16817 button_type = GD_TYPE_CHECK_BUTTON;
16819 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16820 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16821 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16822 event_mask = GD_EVENT_PRESSED;
16823 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
16824 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16827 gi = CreateGadget(GDI_CUSTOM_ID, id,
16828 GDI_INFO_TEXT, gamebutton_info[i].infotext,
16833 GDI_X, DX + gd_xoffset,
16834 GDI_Y, DY + gd_yoffset,
16836 GDI_WIDTH, GAME_BUTTON_XSIZE,
16837 GDI_HEIGHT, GAME_BUTTON_YSIZE,
16838 GDI_TYPE, button_type,
16839 GDI_STATE, GD_BUTTON_UNPRESSED,
16840 GDI_CHECKED, checked,
16841 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
16842 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
16843 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
16844 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
16845 GDI_DIRECT_DRAW, FALSE,
16846 GDI_EVENT_MASK, event_mask,
16847 GDI_CALLBACK_ACTION, HandleGameButtons,
16851 Error(ERR_EXIT, "cannot create gadget");
16853 game_gadget[id] = gi;
16857 void FreeGameButtons()
16861 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16862 FreeGadget(game_gadget[i]);
16865 static void MapGameButtons()
16869 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16870 MapGadget(game_gadget[i]);
16873 void UnmapGameButtons()
16877 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16878 UnmapGadget(game_gadget[i]);
16881 void RedrawGameButtons()
16885 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16886 RedrawGadget(game_gadget[i]);
16889 static void HandleGameButtonsExt(int id)
16891 if (game_status != GAME_MODE_PLAYING)
16896 case GAME_CTRL_ID_STOP:
16900 RequestQuitGame(TRUE);
16903 case GAME_CTRL_ID_PAUSE:
16904 if (options.network)
16906 #if defined(NETWORK_AVALIABLE)
16908 SendToServer_ContinuePlaying();
16910 SendToServer_PausePlaying();
16914 TapeTogglePause(TAPE_TOGGLE_MANUAL);
16917 case GAME_CTRL_ID_PLAY:
16920 #if defined(NETWORK_AVALIABLE)
16921 if (options.network)
16922 SendToServer_ContinuePlaying();
16926 tape.pausing = FALSE;
16927 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16932 case SOUND_CTRL_ID_MUSIC:
16933 if (setup.sound_music)
16935 setup.sound_music = FALSE;
16939 else if (audio.music_available)
16941 setup.sound = setup.sound_music = TRUE;
16943 SetAudioMode(setup.sound);
16949 case SOUND_CTRL_ID_LOOPS:
16950 if (setup.sound_loops)
16951 setup.sound_loops = FALSE;
16952 else if (audio.loops_available)
16954 setup.sound = setup.sound_loops = TRUE;
16956 SetAudioMode(setup.sound);
16960 case SOUND_CTRL_ID_SIMPLE:
16961 if (setup.sound_simple)
16962 setup.sound_simple = FALSE;
16963 else if (audio.sound_available)
16965 setup.sound = setup.sound_simple = TRUE;
16967 SetAudioMode(setup.sound);
16976 static void HandleGameButtons(struct GadgetInfo *gi)
16978 HandleGameButtonsExt(gi->custom_id);
16981 void HandleSoundButtonKeys(Key key)
16984 if (key == setup.shortcut.sound_simple)
16985 ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16986 else if (key == setup.shortcut.sound_loops)
16987 ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16988 else if (key == setup.shortcut.sound_music)
16989 ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16991 if (key == setup.shortcut.sound_simple)
16992 HandleGameButtonsExt(SOUND_CTRL_ID_SIMPLE);
16993 else if (key == setup.shortcut.sound_loops)
16994 HandleGameButtonsExt(SOUND_CTRL_ID_LOOPS);
16995 else if (key == setup.shortcut.sound_music)
16996 HandleGameButtonsExt(SOUND_CTRL_ID_MUSIC);